changeset 12574:04e51b3026c0

Merge.
author Doug Simon <doug.simon@oracle.com>
date Thu, 24 Oct 2013 23:47:41 +0200
parents 6332050441eb (diff) 30810338dfa9 (current diff)
children 8fde330c11cd faded4a83d63
files graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java
diffstat 26 files changed, 308 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java	Thu Oct 24 23:47:41 2013 +0200
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.compiler.test;
 
+import static com.oracle.graal.nodes.ConstantNode.*;
 import static com.oracle.graal.nodes.extended.BranchProbabilityNode.*;
 import static org.junit.Assert.*;
 
@@ -144,7 +145,9 @@
         new ConditionalEliminationPhase(getMetaAccess()).apply(graph);
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null));
         for (ConstantNode constant : graph.getNodes().filter(ConstantNode.class)) {
-            assertTrue("unexpected constant: " + constant, constant.asConstant().isNull() || constant.asConstant().asInt() > 0);
+            if (ConstantNodeRecordsUsages || !constant.gatherUsages().isEmpty()) {
+                assertTrue("unexpected constant: " + constant, constant.asConstant().isNull() || constant.asConstant().asInt() > 0);
+            }
         }
     }
 
@@ -176,7 +179,9 @@
         new ConditionalEliminationPhase(getMetaAccess()).apply(graph);
         new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null));
         for (ConstantNode constant : graph.getNodes().filter(ConstantNode.class)) {
-            assertTrue("unexpected constant: " + constant, constant.asConstant().isNull() || constant.asConstant().asInt() > 0);
+            if (ConstantNodeRecordsUsages || !constant.gatherUsages().isEmpty()) {
+                assertTrue("unexpected constant: " + constant, constant.asConstant().isNull() || constant.asConstant().asInt() > 0);
+            }
         }
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Thu Oct 24 23:47:41 2013 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.compiler.test;
 
 import static com.oracle.graal.api.code.CodeUtil.*;
+import static com.oracle.graal.nodes.ConstantNode.*;
 import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.io.*;
@@ -116,12 +117,32 @@
         assertEquals(expected, graph, false);
     }
 
+    protected int countUnusedConstants(StructuredGraph graph) {
+        int total = 0;
+        for (ConstantNode node : graph.getNodes().filter(ConstantNode.class)) {
+            if (!ConstantNodeRecordsUsages) {
+                if (node.gatherUsages().isEmpty()) {
+                    total++;
+                }
+            } else {
+                if (node.usages().isEmpty()) {
+                    total++;
+                }
+            }
+        }
+        return total;
+    }
+
+    protected int getNodeCountExcludingUnusedConstants(StructuredGraph graph) {
+        return graph.getNodeCount() - countUnusedConstants(graph);
+    }
+
     protected void assertEquals(StructuredGraph expected, StructuredGraph graph, boolean excludeVirtual) {
         String expectedString = getCanonicalGraphString(expected, excludeVirtual);
         String actualString = getCanonicalGraphString(graph, excludeVirtual);
         String mismatchString = "mismatch in graphs:\n========= expected =========\n" + expectedString + "\n\n========= actual =========\n" + actualString;
 
-        if (!excludeVirtual && expected.getNodeCount() != graph.getNodeCount()) {
+        if (!excludeVirtual && getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) {
             Debug.dump(expected, "Node count not matching - expected");
             Debug.dump(graph, "Node count not matching - actual");
             Assert.fail("Graphs do not have the same number of nodes: " + expected.getNodeCount() + " vs. " + graph.getNodeCount() + "\n" + mismatchString);
@@ -161,16 +182,18 @@
             }
             result.append("\n");
             for (Node node : schedule.getBlockToNodesMap().get(block)) {
-                if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode)) {
-                    int id;
-                    if (canonicalId.get(node) != null) {
-                        id = canonicalId.get(node);
-                    } else {
-                        id = nextId++;
-                        canonicalId.set(node, id);
+                if (node.recordsUsages()) {
+                    if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode)) {
+                        int id;
+                        if (canonicalId.get(node) != null) {
+                            id = canonicalId.get(node);
+                        } else {
+                            id = nextId++;
+                            canonicalId.set(node, id);
+                        }
+                        String name = node instanceof ConstantNode ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
+                        result.append("  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + node.usages().count() + ")\n"));
                     }
-                    String name = node instanceof ConstantNode ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
-                    result.append("  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + node.usages().count() + ")\n"));
                 }
             }
         }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java	Thu Oct 24 23:47:41 2013 +0200
@@ -197,7 +197,7 @@
 
     @Override
     protected void assertEquals(StructuredGraph expected, StructuredGraph graph) {
-        if (expected.getNodeCount() != graph.getNodeCount()) {
+        if (getNodeCountExcludingUnusedConstants(expected) != getNodeCountExcludingUnusedConstants(graph)) {
             outputGraph(expected, "expected");
             outputGraph(graph, "actual");
             Assert.fail("Graphs do not have the same number of nodes: " + expected.getNodeCount() + " vs. " + graph.getNodeCount());
@@ -225,7 +225,7 @@
     }
 
     private static void outputNode(Node node) {
-        TTY.print("  " + node + "    (usage count: " + node.usages().count() + ") (inputs:");
+        TTY.print("  " + node + "    (usage count: " + (node.recordsUsages() ? node.usages().count() : "?") + ") (inputs:");
         for (Node input : node.inputs()) {
             TTY.print(" " + input.toString(Verbosity.Id));
         }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/deopt/CompiledMethodTest.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/deopt/CompiledMethodTest.java	Thu Oct 24 23:47:41 2013 +0200
@@ -63,7 +63,7 @@
             if (node instanceof ConstantNode) {
                 ConstantNode constant = (ConstantNode) node;
                 if (constant.kind() == Kind.Object && " ".equals(constant.value.asObject())) {
-                    graph.replaceFloating(constant, ConstantNode.forObject("-", getMetaAccess(), graph));
+                    constant.replace(ConstantNode.forObject("-", getMetaAccess(), graph));
                 }
             }
         }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Thu Oct 24 23:47:41 2013 +0200
@@ -869,8 +869,7 @@
 
         // check that the liveIn set of the first block is empty
         Block startBlock = ir.cfg.getStartBlock();
-        BitSet liveInArgs = new BitSet(blockData.get(startBlock).liveIn.size());
-        if (!blockData.get(startBlock).liveIn.equals(liveInArgs)) {
+        if (blockData.get(startBlock).liveIn.cardinality() != 0) {
             if (DetailedAsserts.getValue()) {
                 reportFailure(numBlocks);
             }
@@ -878,73 +877,76 @@
             TTY.println("preds=" + startBlock.getPredecessorCount() + ", succs=" + startBlock.getSuccessorCount());
             TTY.println("startBlock-ID: " + startBlock.getId());
 
-            // bailout of if this occurs in product mode.
-            throw new GraalInternalError("liveIn set of first block must be empty");
+            // bailout if this occurs in product mode.
+            throw new GraalInternalError("liveIn set of first block must be empty: " + blockData.get(startBlock).liveIn);
         }
     }
 
     private void reportFailure(int numBlocks) {
         TTY.println(gen.getGraph().toString());
-        TTY.println("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined)");
-        TTY.print("affected registers:");
-        TTY.println(blockData.get(ir.cfg.getStartBlock()).liveIn.toString());
+        TTY.println("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):");
+        BitSet startBlockLiveIn = blockData.get(ir.cfg.getStartBlock()).liveIn;
+        for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
+            Value operand = operandFor(operandNum);
+            TTY.println("  var %d; operand=%s; node=%s", operandNum, operand.toString(), gen.valueForOperand(operand));
+        }
 
         // print some additional information to simplify debugging
-        for (int operandNum = 0; operandNum < blockData.get(ir.cfg.getStartBlock()).liveIn.size(); operandNum++) {
-            if (blockData.get(ir.cfg.getStartBlock()).liveIn.get(operandNum)) {
-                Value operand = operandFor(operandNum);
-                TTY.println(" var %d; operand=%s; node=%s", operandNum, operand.toString(), gen.valueForOperand(operand));
+        for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
+            Value operand = operandFor(operandNum);
+            TTY.println("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand.toString(), gen.valueForOperand(operand));
+
+            Deque<Block> definedIn = new ArrayDeque<>();
+            HashSet<Block> usedIn = new HashSet<>();
+            for (Block block : sortedBlocks) {
+                if (blockData.get(block).liveGen.get(operandNum)) {
+                    usedIn.add(block);
+                    TTY.println("used in block B%d {", block.getId());
+                    for (LIRInstruction ins : ir.lir(block)) {
+                        TTY.println("  " + ins.id() + ": " + ins.toString());
+                        ins.forEachState(new ValueProcedure() {
 
-                Deque<Block> definedIn = new ArrayDeque<>();
-                HashSet<Block> usedIn = new HashSet<>();
-                for (Block block : sortedBlocks) {
-                    if (blockData.get(block).liveGen.get(operandNum)) {
-                        usedIn.add(block);
-                        TTY.println("  used in block B%d", block.getId());
-                        for (LIRInstruction ins : ir.lir(block)) {
-                            TTY.println(ins.id() + ": " + ins.toString());
-                            ins.forEachState(new ValueProcedure() {
+                            @Override
+                            public Value doValue(Value liveStateOperand) {
+                                TTY.println("    operand=" + liveStateOperand);
+                                return liveStateOperand;
+                            }
+                        });
+                    }
+                    TTY.println("}");
+                }
+                if (blockData.get(block).liveKill.get(operandNum)) {
+                    definedIn.add(block);
+                    TTY.println("defined in block B%d {", block.getId());
+                    for (LIRInstruction ins : ir.lir(block)) {
+                        TTY.println("  " + ins.id() + ": " + ins.toString());
+                    }
+                    TTY.println("}");
+                }
+            }
 
-                                @Override
-                                public Value doValue(Value liveStateOperand) {
-                                    TTY.println("   operand=" + liveStateOperand);
-                                    return liveStateOperand;
-                                }
-                            });
+            int[] hitCount = new int[numBlocks];
+
+            while (!definedIn.isEmpty()) {
+                Block block = definedIn.removeFirst();
+                usedIn.remove(block);
+                for (Block successor : block.getSuccessors()) {
+                    if (successor.isLoopHeader()) {
+                        if (!block.isLoopEnd()) {
+                            definedIn.add(successor);
                         }
-                    }
-                    if (blockData.get(block).liveKill.get(operandNum)) {
-                        definedIn.add(block);
-                        TTY.println("  defined in block B%d", block.getId());
-                        for (LIRInstruction ins : ir.lir(block)) {
-                            TTY.println(ins.id() + ": " + ins.toString());
+                    } else {
+                        if (++hitCount[successor.getId()] == successor.getPredecessorCount()) {
+                            definedIn.add(successor);
                         }
                     }
                 }
-
-                int[] hitCount = new int[numBlocks];
-
-                while (!definedIn.isEmpty()) {
-                    Block block = definedIn.removeFirst();
-                    usedIn.remove(block);
-                    for (Block successor : block.getSuccessors()) {
-                        if (successor.isLoopHeader()) {
-                            if (!block.isLoopEnd()) {
-                                definedIn.add(successor);
-                            }
-                        } else {
-                            if (++hitCount[successor.getId()] == successor.getPredecessorCount()) {
-                                definedIn.add(successor);
-                            }
-                        }
-                    }
-                }
-                TTY.print("  offending usages are in: ");
-                for (Block block : usedIn) {
-                    TTY.print("B%d ", block.getId());
-                }
-                TTY.println();
             }
+            TTY.print("**** offending usages are in: ");
+            for (Block block : usedIn) {
+                TTY.print("B%d ", block.getId());
+            }
+            TTY.println();
         }
     }
 
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Thu Oct 24 23:47:41 2013 +0200
@@ -26,6 +26,7 @@
 import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.api.meta.Value.*;
 import static com.oracle.graal.lir.LIRValueUtil.*;
+import static com.oracle.graal.nodes.ConstantNode.*;
 import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.util.*;
@@ -66,6 +67,13 @@
     protected final DebugInfoBuilder debugInfoBuilder;
 
     protected Block currentBlock;
+
+    /**
+     * Maps constants the variables within the scope of a single block to avoid loading a constant
+     * more than once per block.
+     */
+    private Map<Constant, Variable> constantsLoadedInCurrentBlock;
+
     private ValueNode currentInstruction;
     private ValueNode lastInstructionPrinted; // Debugging only
 
@@ -151,7 +159,38 @@
         if (nodeOperands == null) {
             return null;
         }
-        return nodeOperands.get(node);
+        Value operand = nodeOperands.get(node);
+        if (operand == null) {
+            return getConstantOperand(node);
+        }
+        return operand;
+    }
+
+    private Value getConstantOperand(ValueNode node) {
+        if (!ConstantNodeRecordsUsages) {
+            Constant value = node.asConstant();
+            if (value != null) {
+                if (canInlineConstant(value)) {
+                    return setResult(node, value);
+                } else {
+                    Variable loadedValue;
+                    if (constantsLoadedInCurrentBlock == null) {
+                        constantsLoadedInCurrentBlock = new HashMap<>();
+                        loadedValue = null;
+                    } else {
+                        loadedValue = constantsLoadedInCurrentBlock.get(value);
+                    }
+                    if (loadedValue == null) {
+                        loadedValue = emitMove(value);
+                        constantsLoadedInCurrentBlock.put(value, loadedValue);
+                    }
+                    return loadedValue;
+                }
+            }
+        } else {
+            // Constant is loaded by ConstantNode.generate()
+        }
+        return null;
     }
 
     public ValueNode valueForOperand(Value value) {
@@ -188,7 +227,7 @@
     @Override
     public Value setResult(ValueNode x, Value operand) {
         assert (!isRegister(operand) || !attributes(asRegister(operand)).isAllocatable());
-        assert operand(x) == null : "operand cannot be set twice";
+        assert nodeOperands == null || nodeOperands.get(x) == null : "operand cannot be set twice";
         assert operand != null && isLegal(operand) : "operand must be legal";
         assert operand.getKind().getStackKind() == x.kind() || x.kind() == Kind.Illegal : operand.getKind().getStackKind() + " must match " + x.kind();
         assert !(x instanceof VirtualObjectNode);
@@ -296,6 +335,8 @@
         }
 
         currentBlock = block;
+        resetLoadedConstants();
+
         // set up the list of LIR instructions
         assert lir.lir(block) == null : "LIR list already computed for this block";
         lir.setLir(block, new ArrayList<LIRInstruction>());
@@ -319,7 +360,9 @@
             if (TraceLIRGeneratorLevel.getValue() >= 3) {
                 TTY.println("LIRGen for " + instr);
             }
-            if (instr instanceof ValueNode) {
+            if (!ConstantNodeRecordsUsages && instr instanceof ConstantNode) {
+                // Loading of constants is done lazily by operand()
+            } else if (instr instanceof ValueNode) {
                 ValueNode valueNode = (ValueNode) instr;
                 if (operand(valueNode) == null) {
                     if (!peephole(valueNode)) {
@@ -355,6 +398,12 @@
         }
     }
 
+    private void resetLoadedConstants() {
+        if (constantsLoadedInCurrentBlock != null && !constantsLoadedInCurrentBlock.isEmpty()) {
+            constantsLoadedInCurrentBlock.clear();
+        }
+    }
+
     protected abstract boolean peephole(ValueNode valueNode);
 
     private boolean endsWithJump(Block block) {
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Thu Oct 24 23:47:41 2013 +0200
@@ -361,7 +361,7 @@
             int minCount = Integer.MAX_VALUE;
             Node minCountNode = null;
             for (Node input : node.inputs()) {
-                if (input != null) {
+                if (input != null && input.recordsUsages()) {
                     int estimate = input.getUsageCountUpperBound();
                     if (estimate == 0) {
                         return null;
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Thu Oct 24 23:47:41 2013 +0200
@@ -255,6 +255,7 @@
     }
 
     int getUsageCountUpperBound() {
+        assert recordsUsages();
         if (usage0 == null) {
             return 0;
         }
@@ -265,13 +266,23 @@
     }
 
     /**
-     * Gets the list of nodes that use this node (e.g., as an input).
+     * Gets the list of nodes that use this node (i.e., as an input).
      */
     public final NodeIterable<Node> usages() {
+        assert recordsUsages() : this;
         return new NodeUsageIterable();
     }
 
     /**
+     * Determines if this node records its usages (i.e. the nodes for which it is an input). All
+     * methods in {@link Node} that pertain to querying or updating usage information must not be
+     * called for a {@link Node} instance that returns false for this method.
+     */
+    public boolean recordsUsages() {
+        return true;
+    }
+
+    /**
      * Finds the index of the last non-null entry in a node array. The search assumes that all
      * non-null entries precede the first null entry in the array.
      * 
@@ -311,6 +322,7 @@
      * @param node the node to add
      */
     private void addUsage(Node node) {
+        assert recordsUsages();
         incUsageModCount();
         if (usage0 == null) {
             usage0 = node;
@@ -342,6 +354,7 @@
      * @return whether or not {@code usage} was in the usage list
      */
     private boolean removeUsage(Node node) {
+        assert recordsUsages();
         // It is critical that this method maintains the invariant that
         // the usage list has no null element preceding a non-null element
         incUsageModCount();
@@ -392,6 +405,7 @@
     }
 
     private void clearUsages() {
+        assert recordsUsages();
         incUsageModCount();
         usage0 = null;
         usage1 = null;
@@ -443,16 +457,20 @@
     protected void updateUsages(Node oldInput, Node newInput) {
         if (oldInput != newInput) {
             if (oldInput != null) {
-                boolean result = removeThisFromUsages(oldInput);
-                assert assertTrue(result, "not found in usages, old input: %s", oldInput);
+                if (oldInput.recordsUsages()) {
+                    boolean result = removeThisFromUsages(oldInput);
+                    assert assertTrue(result, "not found in usages, old input: %s", oldInput);
+                }
             }
             if (newInput != null) {
-                NodeChangedListener listener = graph.inputChangedListener;
-                if (listener != null) {
-                    listener.nodeChanged(this);
+                if (newInput.recordsUsages()) {
+                    NodeChangedListener listener = graph.inputChangedListener;
+                    if (listener != null) {
+                        listener.nodeChanged(this);
+                    }
+                    newInput.addUsage(this);
                 }
-                newInput.addUsage(this);
-            } else if (oldInput != null && oldInput.usages().isEmpty()) {
+            } else if (oldInput != null && oldInput.recordsUsages() && oldInput.usages().isEmpty()) {
                 NodeChangedListener listener = graph.usagesDroppedToZeroListener;
                 if (listener != null) {
                     listener.nodeChanged(oldInput);
@@ -513,7 +531,9 @@
                 if (listener != null) {
                     listener.nodeChanged(usage);
                 }
-                other.addUsage(usage);
+                if (other.recordsUsages()) {
+                    other.addUsage(usage);
+                }
             }
         }
         clearUsages();
@@ -554,11 +574,13 @@
         assert assertFalse(isDeleted(), "cannot clear inputs of deleted node");
 
         for (Node input : inputs()) {
-            removeThisFromUsages(input);
-            if (input.usages().isEmpty()) {
-                NodeChangedListener listener = graph.usagesDroppedToZeroListener;
-                if (listener != null) {
-                    listener.nodeChanged(input);
+            if (input.recordsUsages()) {
+                removeThisFromUsages(input);
+                if (input.usages().isEmpty()) {
+                    NodeChangedListener listener = graph.usagesDroppedToZeroListener;
+                    if (listener != null) {
+                        listener.nodeChanged(input);
+                    }
                 }
             }
         }
@@ -580,7 +602,9 @@
     }
 
     private boolean checkDeletion() {
-        assertTrue(usages().isEmpty(), "cannot delete node %s because of usages: %s", this, usages());
+        if (recordsUsages()) {
+            assertTrue(usages().isEmpty(), "cannot delete node %s because of usages: %s", this, usages());
+        }
         assertTrue(predecessor == null, "cannot delete node %s because of predecessor: %s", this, predecessor);
         return true;
     }
@@ -603,7 +627,9 @@
         NodeClass clazz = getNodeClass();
         clazz.copyInputs(this, newNode);
         for (Node input : inputs()) {
-            input.addUsage(newNode);
+            if (input.recordsUsages()) {
+                input.addUsage(newNode);
+            }
         }
         return newNode;
     }
@@ -676,16 +702,18 @@
         assertTrue(isAlive(), "cannot verify inactive nodes (id=%d)", id);
         assertTrue(graph() != null, "null graph");
         for (Node input : inputs()) {
-            assertTrue(input.usages().contains(this), "missing usage in input %s", input);
+            assertTrue(!input.recordsUsages() || input.usages().contains(this), "missing usage in input %s", input);
             assertTrue(input.graph() == graph(), "mismatching graph in input %s", input);
         }
         for (Node successor : successors()) {
             assertTrue(successor.predecessor() == this, "missing predecessor in %s (actual: %s)", successor, successor.predecessor());
             assertTrue(successor.graph() == graph(), "mismatching graph in successor %s", successor);
         }
-        for (Node usage : usages()) {
-            assertFalse(usage.isDeleted(), "usage must never be deleted");
-            assertTrue(usage.inputs().contains(this), "missing input in usage %s", usage);
+        if (recordsUsages()) {
+            for (Node usage : usages()) {
+                assertFalse(usage.isDeleted(), "usage must never be deleted");
+                assertTrue(usage.inputs().contains(this), "missing input in usage %s", usage);
+            }
         }
         if (predecessor != null) {
             assertFalse(predecessor.isDeleted(), "predecessor must never be deleted");
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Thu Oct 24 23:47:41 2013 +0200
@@ -1315,7 +1315,7 @@
                     newNodes.put(node, replacement);
                 } else {
                     Node newNode = node.clone(graph, false);
-                    assert newNode.usages().count() == 0 || newNode.inputs().count() == 0;
+                    assert newNode.inputs().count() == 0 || newNode.usages().count() == 0;
                     assert newNode.getClass() == node.getClass();
                     newNodes.put(node, newNode);
                 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java	Thu Oct 24 23:47:41 2013 +0200
@@ -174,7 +174,7 @@
         if (value != null) {
             OptionValue<?> optionValue = desc.getOptionValue();
             optionValue.setValue(value);
-            // Logger.info("Set option " + fieldName + " to " + value);
+            // Logger.info("Set option " + desc.getName() + " to " + value);
         } else {
             Logger.info("Wrong value \"" + valueString + "\" for option " + optionName);
             return false;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/AheadOfTimeVerificationPhase.java	Thu Oct 24 23:47:41 2013 +0200
@@ -39,7 +39,9 @@
     @Override
     protected boolean verify(StructuredGraph graph, PhaseContext context) {
         for (ConstantNode node : graph.getNodes().filter(ConstantNode.class)) {
-            assert !isObject(node) || isNullReference(node) || isInternedString(node) : "illegal object constant: " + node;
+            if (node.recordsUsages() || !node.gatherUsages().isEmpty()) {
+                assert !isObject(node) || isNullReference(node) || isInternedString(node) : "illegal object constant: " + node;
+            }
         }
         return true;
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/LoadJavaMirrorWithKlassPhase.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/LoadJavaMirrorWithKlassPhase.java	Thu Oct 24 23:47:41 2013 +0200
@@ -68,7 +68,7 @@
                 LocationNode location = graph.unique(ConstantLocationNode.create(FINAL_LOCATION, stamp.kind(), classMirrorOffset, graph));
                 FloatingReadNode freadNode = graph.unique(new FloatingReadNode(klassNode, location, null, stamp));
 
-                graph.replaceFloating(node, freadNode);
+                node.replace(freadNode);
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java	Thu Oct 24 23:47:41 2013 +0200
@@ -45,6 +45,17 @@
     }
 
     /**
+     * Used to measure the impact of ConstantNodes not recording their usages. This and all code
+     * predicated on this value being true will be removed at some point.
+     */
+    public static final boolean ConstantNodeRecordsUsages = Boolean.getBoolean("graal.constantNodeRecordsUsages");
+
+    @Override
+    public boolean recordsUsages() {
+        return ConstantNodeRecordsUsages;
+    }
+
+    /**
      * Constructs a new ConstantNode representing the specified constant.
      * 
      * @param value the constant
@@ -54,6 +65,35 @@
         this.value = value;
     }
 
+    /**
+     * Computes the usages of this node by iterating over all the nodes in the graph, searching for
+     * those that have this node as an input.
+     */
+    public List<Node> gatherUsages() {
+        assert !ConstantNodeRecordsUsages;
+        List<Node> usages = new ArrayList<>();
+        for (Node node : graph().getNodes()) {
+            for (Node input : node.inputs()) {
+                if (input == this) {
+                    usages.add(node);
+                }
+            }
+        }
+        return usages;
+    }
+
+    public void replace(Node replacement) {
+        if (!recordsUsages()) {
+            List<Node> usages = gatherUsages();
+            for (Node usage : usages) {
+                usage.replaceFirstInput(this, replacement);
+            }
+            graph().removeFloating(this);
+        } else {
+            graph().replaceFloating(this, replacement);
+        }
+    }
+
     @Override
     public void generate(LIRGeneratorTool gen) {
         if (gen.canInlineConstant(value) || onlyUsedInVirtualState()) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java	Thu Oct 24 23:47:41 2013 +0200
@@ -90,7 +90,7 @@
             }
             ValueNode removedValue = phi.valueAt(predIndex);
             phi.removeInput(predIndex);
-            if (removedValue != null && removedValue.isAlive() && removedValue.usages().isEmpty() && GraphUtil.isFloatingNode().apply(removedValue)) {
+            if (removedValue != null && removedValue.isAlive() && removedValue.recordsUsages() && removedValue.usages().isEmpty() && GraphUtil.isFloatingNode().apply(removedValue)) {
                 GraphUtil.killWithUnusedFloatingInputs(removedValue);
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Thu Oct 24 23:47:41 2013 +0200
@@ -123,13 +123,17 @@
     }
 
     public static void killWithUnusedFloatingInputs(Node node) {
-        List<Node> floatingInputs = node.inputs().filter(isFloatingNode()).snapshot();
-        node.safeDelete();
+        if (node.recordsUsages()) {
+            List<Node> floatingInputs = node.inputs().filter(isFloatingNode()).snapshot();
+            node.safeDelete();
 
-        for (Node in : floatingInputs) {
-            if (in.isAlive() && in.usages().isEmpty()) {
-                killWithUnusedFloatingInputs(in);
+            for (Node in : floatingInputs) {
+                if (in.isAlive() && (!in.recordsUsages() || in.usages().isEmpty())) {
+                    killWithUnusedFloatingInputs(in);
+                }
             }
+        } else {
+            assert node.inputs().isEmpty();
         }
     }
 
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java	Thu Oct 24 23:47:41 2013 +0200
@@ -206,7 +206,7 @@
         }
 
         private static boolean tryKillUnused(Node node) {
-            if (node.isAlive() && GraphUtil.isFloatingNode().apply(node) && node.usages().isEmpty()) {
+            if (node.isAlive() && GraphUtil.isFloatingNode().apply(node) && node.recordsUsages() && node.usages().isEmpty()) {
                 GraphUtil.killWithUnusedFloatingInputs(node);
                 return true;
             }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Thu Oct 24 23:47:41 2013 +0200
@@ -631,8 +631,10 @@
             cdbc.apply(cfg.getNodeToBlock().get(succ));
         }
         ensureScheduledUsages(node, strategy);
-        for (Node usage : node.usages()) {
-            blocksForUsage(node, usage, cdbc, strategy);
+        if (node.recordsUsages()) {
+            for (Node usage : node.usages()) {
+                blocksForUsage(node, usage, cdbc, strategy);
+            }
         }
         List<FixedNode> usages = phantomUsages.get(node);
         if (usages != null) {
@@ -808,8 +810,10 @@
     }
 
     private void ensureScheduledUsages(Node node, SchedulingStrategy strategy) {
-        for (Node usage : node.usages().filter(ScheduledNode.class)) {
-            assignBlockToNode((ScheduledNode) usage, strategy);
+        if (node.recordsUsages()) {
+            for (Node usage : node.usages().filter(ScheduledNode.class)) {
+                assignBlockToNode((ScheduledNode) usage, strategy);
+            }
         }
         // now true usages are ready
     }
@@ -1057,14 +1061,16 @@
             }
 
             visited.mark(instruction);
-            for (Node usage : instruction.usages()) {
-                if (usage instanceof VirtualState) {
-                    // only fixed nodes can have VirtualState -> no need to schedule them
-                } else {
-                    if (instruction instanceof LoopExitNode && usage instanceof ProxyNode) {
-                        // value proxies should be scheduled before the loopexit, not after
+            if (instruction.recordsUsages()) {
+                for (Node usage : instruction.usages()) {
+                    if (usage instanceof VirtualState) {
+                        // only fixed nodes can have VirtualState -> no need to schedule them
                     } else {
-                        addToEarliestSorting(b, (ScheduledNode) usage, sortedInstructions, visited);
+                        if (instruction instanceof LoopExitNode && usage instanceof ProxyNode) {
+                            // value proxies should be scheduled before the loopexit, not after
+                        } else {
+                            addToEarliestSorting(b, (ScheduledNode) usage, sortedInstructions, visited);
+                        }
                     }
                 }
             }
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java	Thu Oct 24 23:47:41 2013 +0200
@@ -327,7 +327,7 @@
         out.println("=== Succesors ===");
         printNamedNodes(node, node.successors().iterator(), "", "\n", null);
         out.println("=== Usages ===");
-        if (!node.usages().isEmpty()) {
+        if (node.recordsUsages() && !node.usages().isEmpty()) {
             for (Node usage : node.usages()) {
                 out.print(nodeToString(usage)).print(" ");
             }
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinterObserver.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinterObserver.java	Thu Oct 24 23:47:41 2013 +0200
@@ -94,7 +94,7 @@
             return false;
         }
 
-        if (method != curMethod || !curDecorators.equals(decorators)) {
+        if (!method.equals(curMethod) || !curDecorators.equals(decorators)) {
             cfgPrinter.printCompilation(method);
             TTY.println("CFGPrinter: Dumping method %s to %s", method, cfgFile);
             curMethod = method;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java	Thu Oct 24 23:47:41 2013 +0200
@@ -321,7 +321,8 @@
     }
 
     public void cleanUpReturnCheckCast(Node newInstance) {
-        if (newInstance instanceof ValueNode && (((ValueNode) newInstance).kind() != Kind.Object || ((ValueNode) newInstance).stamp() == StampFactory.forNodeIntrinsic())) {
+        if (newInstance.recordsUsages() && newInstance instanceof ValueNode &&
+                        (((ValueNode) newInstance).kind() != Kind.Object || ((ValueNode) newInstance).stamp() == StampFactory.forNodeIntrinsic())) {
             StructuredGraph graph = (StructuredGraph) newInstance.graph();
             for (CheckCastNode checkCastNode : newInstance.usages().filter(CheckCastNode.class).snapshot()) {
                 for (Node checkCastUsage : checkCastNode.usages().snapshot()) {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Thu Oct 24 23:47:41 2013 +0200
@@ -273,6 +273,20 @@
         }
     }
 
+    static class VarargsPlaceholderNode extends FloatingNode implements ArrayLengthProvider {
+
+        final Varargs varargs;
+
+        public VarargsPlaceholderNode(Varargs varargs, MetaAccessProvider metaAccess) {
+            super(StampFactory.exactNonNull(metaAccess.lookupJavaType(varargs.componentType).getArrayClass()));
+            this.varargs = varargs;
+        }
+
+        public ValueNode length() {
+            return ConstantNode.forInt(varargs.length, graph());
+        }
+    }
+
     static class CacheKey {
 
         private final ResolvedJavaMethod method;
@@ -411,7 +425,7 @@
         assert checkTemplate(metaAccess, args, method, signature);
 
         int parameterCount = args.info.getParameterCount();
-        ConstantNode[] placeholders = new ConstantNode[parameterCount];
+        VarargsPlaceholderNode[] placeholders = new VarargsPlaceholderNode[parameterCount];
 
         for (int i = 0; i < parameterCount; i++) {
             if (args.info.isConstantParameter(i)) {
@@ -426,8 +440,7 @@
                 nodeReplacements.put(snippetGraph.getLocal(i), ConstantNode.forConstant(constantArg, metaAccess, snippetCopy));
             } else if (args.info.isVarargsParameter(i)) {
                 Varargs varargs = (Varargs) args.values[i];
-                Object array = Array.newInstance(varargs.componentType, varargs.length);
-                ConstantNode placeholder = ConstantNode.forObject(array, metaAccess, snippetCopy);
+                VarargsPlaceholderNode placeholder = snippetCopy.unique(new VarargsPlaceholderNode(varargs, providers.getMetaAccess()));
                 nodeReplacements.put(snippetGraph.getLocal(i), placeholder);
                 placeholders[i] = placeholder;
             }
@@ -461,7 +474,7 @@
                 }
                 parameters[i] = locals;
 
-                ConstantNode placeholder = placeholders[i];
+                VarargsPlaceholderNode placeholder = placeholders[i];
                 assert placeholder != null;
                 for (Node usage : placeholder.usages().snapshot()) {
                     if (usage instanceof LoadIndexedNode) {
@@ -584,7 +597,7 @@
         this.returnNode = retNode;
     }
 
-    private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, ConstantNode[] placeholders) {
+    private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, VarargsPlaceholderNode[] placeholders) {
         for (int i = 0; i < parameterCount; i++) {
             if (placeholders[i] != null) {
                 assert placeholders[i].isDeleted() : placeholders[i];
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Thu Oct 24 23:47:41 2013 +0200
@@ -237,13 +237,12 @@
                 ValueNode arg = arguments.get(local.index());
                 if (arg.isConstant()) {
                     Constant constant = arg.asConstant();
-                    ConstantNode constantNode = ConstantNode.forConstant(constant, phaseContext.getMetaAccess(), graphCopy);
-                    local.replaceAndDelete(constantNode);
-                    for (Node usage : constantNode.usages()) {
+                    for (Node usage : local.usages()) {
                         if (usage instanceof Canonicalizable) {
                             modifiedNodes.add(usage);
                         }
                     }
+                    local.replaceAndDelete(ConstantNode.forConstant(constant, phaseContext.getMetaAccess(), graphCopy));
                 }
             }
             Debug.scope("TruffleUnrollLoop", targetMethod, new Runnable() {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Thu Oct 24 23:47:41 2013 +0200
@@ -156,7 +156,7 @@
                             int beforeInvokeMark = graph.getMark();
                             expandInvoke(methodCallTarget);
                             for (Node arg : argumentSnapshot) {
-                                if (arg != null) {
+                                if (arg != null && arg.recordsUsages()) {
                                     for (Node argUsage : arg.usages()) {
                                         if (graph.isNew(beforeInvokeMark, argUsage) && argUsage instanceof Canonicalizable) {
                                             canonicalizerUsages.add(argUsage);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Thu Oct 24 23:47:41 2013 +0200
@@ -531,8 +531,10 @@
     void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node) {
         if (node.isAlive()) {
             aliases.set(node, virtual);
-            for (Node usage : node.usages()) {
-                markVirtualUsages(usage);
+            if (node.recordsUsages()) {
+                for (Node usage : node.usages()) {
+                    markVirtualUsages(usage);
+                }
             }
         }
     }
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java	Thu Oct 24 22:28:09 2013 +0200
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java	Thu Oct 24 23:47:41 2013 +0200
@@ -61,6 +61,9 @@
         wordAccess.inferStamps(graph);
 
         for (ValueNode node : graph.getNodes().filter(ValueNode.class)) {
+            if (!node.recordsUsages()) {
+                continue;
+            }
             for (Node usage : node.usages()) {
                 if (usage instanceof AccessMonitorNode) {
                     verify(!isWord(node), node, usage, "word value has no monitor");
--- a/mxtool/mx.py	Thu Oct 24 22:28:09 2013 +0200
+++ b/mxtool/mx.py	Thu Oct 24 23:47:41 2013 +0200
@@ -768,7 +768,7 @@
             abort('Missing "suite=<name>" in ' + projectsFile)
 
     def _commands_name(self):
-        return 'mx_' + self.name.replace('-','_')
+        return 'mx_' + self.name.replace('-', '_')
 
     def _find_commands(self, name):
         commandsPath = join(self.mxDir, name + '.py')