changeset 5218:c4696edb6e95

Merge.
author Doug Simon <doug.simon@oracle.com>
date Tue, 10 Apr 2012 12:22:46 +0200
parents b64933dc4830 (diff) 70777e50f1e6 (current diff)
children ddccd4abdb09
files
diffstat 46 files changed, 1889 insertions(+), 171 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Apr 10 12:22:46 2012 +0200
@@ -128,46 +128,38 @@
 
         new PhiStampPhase().apply(graph);
 
+        if (GraalOptions.ProbabilityAnalysis && graph.start().probability() == 0) {
+            new ComputeProbabilityPhase().apply(graph);
+        }
+
+        if (GraalOptions.PropagateTypes) {
+            new PropagateTypeCachePhase(target, runtime, assumptions).apply(graph);
+        }
+
         if (GraalOptions.OptCanonicalizer) {
             new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
         }
 
-        if (GraalOptions.ProbabilityAnalysis && graph.start().probability() == 0) {
-            new ComputeProbabilityPhase().apply(graph);
-        }
-
         if (GraalOptions.Intrinsify) {
             new IntrinsificationPhase(runtime).apply(graph);
         }
 
-        if (GraalOptions.PropagateTypes) {
-            if (GraalOptions.OptCanonicalizer) {
-                new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
-            }
-
-            new PropagateTypeCachePhase(target, runtime, assumptions).apply(graph);
-        }
-
         if (GraalOptions.Inline && !plan.isPhaseDisabled(InliningPhase.class)) {
             new InliningPhase(target, runtime, null, assumptions, cache, plan, optimisticOpts).apply(graph);
             new DeadCodeEliminationPhase().apply(graph);
             new PhiStampPhase().apply(graph);
-        }
+            if (GraalOptions.PropagateTypes) {
+                new PropagateTypeCachePhase(target, runtime, assumptions).apply(graph);
+            }
 
-        if (GraalOptions.OptCanonicalizer) {
-            new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
+            if (GraalOptions.OptCanonicalizer) {
+                new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
+            }
         }
 
-        if (GraalOptions.PropagateTypes) {
-            new PropagateTypeCachePhase(target, runtime, assumptions).apply(graph);
-        }
 
         plan.runPhases(PhasePosition.HIGH_LEVEL, graph);
 
-        if (GraalOptions.OptLoops) {
-            new SafepointPollingEliminationPhase().apply(graph);
-        }
-
         if (GraalOptions.EscapeAnalysis && !plan.isPhaseDisabled(EscapeAnalysisPhase.class)) {
             new EscapeAnalysisPhase(target, runtime, assumptions, cache, plan, optimisticOpts).apply(graph);
             new PhiStampPhase().apply(graph);
@@ -175,7 +167,18 @@
                 new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
             }
         }
-
+        if (GraalOptions.OptLoops) {
+            if (GraalOptions.OptLoopTransform) {
+                new LoopTransformPhase().apply(graph);
+            }
+            if (GraalOptions.OptSafepointElimination) {
+                new SafepointPollingEliminationPhase().apply(graph);
+            }
+        }
+        new RemoveValueProxyPhase().apply(graph);
+        if (GraalOptions.OptCanonicalizer) {
+            new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
+        }
         if (GraalOptions.OptGVN) {
             new GlobalValueNumberingPhase().apply(graph);
         }
@@ -197,6 +200,9 @@
         if (GraalOptions.PropagateTypes) {
             new PropagateTypeCachePhase(target, runtime, assumptions).apply(graph);
         }
+        if (GraalOptions.OptCanonicalizer) {
+            new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
+        }
         if (GraalOptions.OptGVN) {
             new GlobalValueNumberingPhase().apply(graph);
         }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java	Tue Apr 10 12:22:46 2012 +0200
@@ -98,6 +98,9 @@
     //rematerialize settings
     public static float   MinimumUsageProbability            = 0.95f;
 
+    //loop transform settings
+    public static float   MinimumPeelProbability             = 0.25f;
+
     // debugging settings
     public static int     MethodEndBreakpointGuards          = 0;
     public static boolean ZapStackOnMethodEntry              = ____;
@@ -143,6 +146,7 @@
     public static boolean PrintAssembly                      = ____;
     public static boolean PrintCodeBytes                     = ____;
     public static int     PrintAssemblyBytesPerLine          = 16;
+    public static boolean PrintBailout                       = ____;
     public static int     TraceLinearScanLevel               = 0;
     public static boolean TraceRegisterAllocation            = false;
     public static int     TraceLIRGeneratorLevel             = 0;
@@ -192,12 +196,14 @@
     public static boolean OptReadElimination                 = true;
     public static boolean OptGVN                             = true;
     public static boolean OptCanonicalizer                   = true;
-    public static boolean OptLoops                           = ____;
+    public static boolean OptLoops                           = true;
     public static boolean ScheduleOutOfLoops                 = true;
     public static boolean OptReorderLoops                    = true;
     public static boolean OptEliminateGuards                 = true;
     public static boolean OptImplicitNullChecks              = true;
     public static boolean OptLivenessAnalysis                = true;
+    public static boolean OptLoopTransform                   = true;
+    public static boolean OptSafepointElimination            = true;
 
     /**
      * Flag to turn on SSA-based register allocation, which is currently under development.
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Tue Apr 10 12:22:46 2012 +0200
@@ -860,7 +860,7 @@
         for (int operandNum = 0; operandNum < blockData.get(ir.cfg.getStartBlock()).liveIn.size(); operandNum++) {
             if (blockData.get(ir.cfg.getStartBlock()).liveIn.get(operandNum)) {
                 CiValue operand = operandFor(operandNum);
-                TTY.println(" var %d; operand=%s", operandNum, operand.toString());
+                TTY.println(" var %d; operand=%s; node=%s", operandNum, operand.toString(), gen.valueForOperand(operand));
 
                 for (int j = 0; j < numBlocks; j++) {
                     Block block = blockAt(j);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java	Tue Apr 10 12:22:46 2012 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.virtual.*;
 
 public class DebugInfoBuilder {
@@ -54,7 +55,13 @@
             FrameState current = topState;
             do {
                 for (Node n : current.virtualObjectMappings()) {
-                    VirtualObjectFieldNode field = (VirtualObjectFieldNode) n;
+                    Node p = n;
+                    while (p instanceof PhiNode) {
+                        PhiNode phi = (PhiNode) p;
+                        assert phi.type() == PhiType.Virtual;
+                        p = phi.valueAt(0);
+                    }
+                    VirtualObjectFieldNode field = (VirtualObjectFieldNode) p;
                     // null states occur for objects with 0 fields
                     if (field != null && !objectStates.containsKey(field.object())) {
                         objectStates.put(field.object(), field);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue Apr 10 12:22:46 2012 +0200
@@ -27,6 +27,7 @@
 import static com.oracle.max.cri.ci.CiValue.*;
 
 import java.util.*;
+import java.util.Map.Entry;
 
 import com.oracle.graal.compiler.*;
 import com.oracle.graal.compiler.util.*;
@@ -173,6 +174,15 @@
         return nodeOperands.get(node);
     }
 
+    public ValueNode valueForOperand(CiValue value) {
+        for (Entry<Node, CiValue> entry : nodeOperands.entries()) {
+            if (entry.getValue() == value) {
+                return (ValueNode) entry.getKey();
+            }
+        }
+        return null;
+    }
+
     /**
      * Creates a new {@linkplain Variable variable}.
      * @param kind The kind of the new variable.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/loop/LoopTransformDataResolver.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.loop;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.NodeClass.NodeClassIterator;
+import com.oracle.graal.graph.NodeClass.Position;
+import com.oracle.graal.nodes.*;
+
+
+
+public class LoopTransformDataResolver {
+    private List<ResolvableSuperBlock> resolvables = new LinkedList<>();
+
+    private abstract static class ResolvableSuperBlock {
+        final SuperBlock block;
+        public ResolvableSuperBlock(SuperBlock block) {
+            this.block = block;
+        }
+        public abstract void resolve();
+    }
+
+    private static class PeeledResolvableSuperBlock extends ResolvableSuperBlock{
+        final SuperBlock peel;
+        final boolean nextIteration;
+        public PeeledResolvableSuperBlock(SuperBlock peeled, SuperBlock peel, boolean nextIteration) {
+            super(peeled);
+            this.peel = peel;
+            this.nextIteration = nextIteration;
+        }
+        @Override
+        public void resolve() {
+            if (nextIteration) {
+                SuperBlock peeled = block;
+                LoopBeginNode loopBegin = (LoopBeginNode) peeled.getEntry();
+                Map<Node, Node> dup = peel.getDuplicationMapping();
+                List<PhiNode> newPhis = new LinkedList<>();
+                for (PhiNode phi : loopBegin.phis().snapshot()) {
+                    ValueNode first = null;
+                    if (loopBegin.loopEnds().count() == 1) {
+                        ValueNode b = phi.valueAt(loopBegin.loopEnds().first()); // back edge value
+                        first = prim(b); // corresponding value in the peel
+                    } else {
+                        Map<EndNode, LoopEndNode> reverseEnds = new HashMap<>(); // map peel's exit to the corresponding loop exits
+                        MergeNode merge = null; // look for the merge if the peel's exits
+                        for (LoopEndNode end : loopBegin.loopEnds()) {
+                            EndNode newEnd = (EndNode) dup.get(end);
+                            if (newEnd != null) {
+                                reverseEnds.put(newEnd, end);
+                                if (prim(phi.valueAt(end)) != null) {
+                                    merge = newEnd.merge();
+                                }
+                            }
+                        }
+                        if (merge != null) { // found values of interest (backedge values that exist in the peel)
+                            PhiNode firstPhi = loopBegin.graph().add(new PhiNode(phi.kind(), merge, phi.type()));
+                            for (EndNode end : merge.forwardEnds()) {
+                                LoopEndNode loopEnd = reverseEnds.get(end);
+                                ValueNode prim = prim(phi.valueAt(loopEnd));
+                                assert prim != null;
+                                firstPhi.addInput(prim);
+                            }
+                            first = firstPhi;
+                            merge.stateAfter().replaceFirstInput(phi, firstPhi); // fix the merge's state after (see SuperBlock.mergeExits)
+                        }
+                    }
+                    if (first != null) { // create a new phi (we don't patch the old one since some usages of the old one may still be valid)
+                        PhiNode newPhi = loopBegin.graph().add(new PhiNode(phi.kind(), loopBegin, phi.type()));
+                        newPhi.addInput(first);
+                        for (LoopEndNode end : loopBegin.orderedLoopEnds()) {
+                            newPhi.addInput(phi.valueAt(end));
+                        }
+                        dup.put(phi, newPhi);
+                        newPhis.add(newPhi);
+                        for (Node usage : phi.usages().snapshot()) {
+                            if (dup.get(usage) != null) { // patch only usages that should use the new phi ie usages that were peeled
+                                usage.replaceFirstInput(phi, newPhi);
+                            }
+                        }
+                    }
+                }
+                // check new phis to see if they have as input some old phis, replace those inputs with the new corresponding phis
+                for (PhiNode phi : newPhis) {
+                    for (int i = 0; i < phi.valueCount(); i++) {
+                        ValueNode v = phi.valueAt(i);
+                        if (loopBegin.isPhiAtMerge(v)) {
+                            PhiNode newV = (PhiNode) dup.get(v);
+                            if (newV != null) {
+                                phi.setValueAt(i, newV);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Gets the corresponding value in the peel.
+         * @param b original value
+         * @return corresponding value in the peel
+         */
+        public ValueNode prim(ValueNode b) {
+            SuperBlock peeled = block;
+            LoopBeginNode loopBegin = (LoopBeginNode) peeled.getEntry();
+            Map<Node, Node> dup = peel.getDuplicationMapping();
+            if (loopBegin.isPhiAtMerge(b)) {
+                PhiNode phi = (PhiNode) b;
+                return phi.valueAt(loopBegin.forwardEnd());
+            } else {
+                ValueNode v = (ValueNode) dup.get(b);
+                if (v == null && nextIteration) {
+                    // may not be right in inversion case
+                    return b;
+                }
+                return v;
+            }
+        }
+    }
+
+    private static class PeelResolvableSuperBlock extends ResolvableSuperBlock{
+        final SuperBlock peeled;
+        public PeelResolvableSuperBlock(SuperBlock peel, SuperBlock peeled) {
+            super(peel);
+            this.peeled = peeled;
+        }
+        @Override
+        public void resolve() {
+            SuperBlock peel = block;
+            LoopBeginNode loopBegin = (LoopBeginNode) peeled.getEntry();
+            for (Entry<Node, Node> entry : peel.getDuplicationMapping().entrySet()) {
+                Node oriNode = entry.getKey();
+                Node newNode = entry.getValue();
+                for (NodeClassIterator iter = oriNode.inputs().iterator(); iter.hasNext();) {
+                    Position pos = iter.nextPosition();
+                    if (pos.isValidFor(newNode, oriNode) && pos.get(newNode) == null) {
+                        Node oriInput = pos.get(oriNode);
+                        // oriInput is not checked against null because oriNode.inputs().iterator() only iterates over non-null pos/input
+                        Node v;
+                        if (loopBegin.isPhiAtMerge(oriInput)) {
+                            PhiNode phi = (PhiNode) oriInput;
+                            v = phi.valueAt(loopBegin.forwardEnd());
+                        } else {
+                            v = oriInput;
+                        }
+                        pos.set(newNode, v);
+                    }
+                }
+            }
+        }
+    }
+
+    public class WholeLoop {
+        private final SuperBlock from;
+        public WholeLoop(SuperBlock from) {
+            this.from = from;
+        }
+        public void peeled(SuperBlock peel) {
+            resolvables.add(new PeelResolvableSuperBlock(peel, from));
+            resolvables.add(new PeeledResolvableSuperBlock(from, peel, true));
+        }
+
+    }
+
+    public void resolve() {
+        for (ResolvableSuperBlock resolvable : this.resolvables) {
+            resolvable.resolve();
+        }
+    }
+
+    public WholeLoop wholeLoop(SuperBlock block) {
+        return new WholeLoop(block);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/loop/LoopTransformUtil.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.loop;
+
+import java.util.*;
+
+import com.oracle.graal.lir.cfg.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.util.*;
+
+
+public class LoopTransformUtil {
+
+    public static void peel(Loop loop) {
+        GraphUtil.normalizeLoopBegin(loop.loopBegin());
+        SuperBlock block = wholeLoop(loop);
+        SuperBlock peel = block.duplicate(); // duplicates the nodes, merges early exits
+
+        peel.insertBefore(loop.loopBegin().forwardEnd()); // connects peeled part's CFG
+
+        LoopTransformDataResolver resolver = new LoopTransformDataResolver();
+        resolver.wholeLoop(block).peeled(peel); // block (comming from the loop) was peeled into peel
+        resolver.resolve();
+
+        peel.finish();
+    }
+
+    public static SuperBlock wholeLoop(Loop loop) {
+        List<BeginNode> blocks = new LinkedList<>();
+        List<BeginNode> earlyExits = new LinkedList<>();
+        for (Block b : loop.blocks) {
+            blocks.add(b.getBeginNode());
+        }
+        for (Block b : loop.exits) {
+            earlyExits.add(b.getBeginNode());
+        }
+        return new SuperBlock(loop.loopBegin(), loop.loopBegin(), blocks, earlyExits, loop.loopBegin());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/loop/SuperBlock.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.loop;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.PhiNode.PhiType;
+import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.nodes.virtual.*;
+
+public class SuperBlock {
+    protected BeginNode entry;
+    protected BeginNode exit;
+    protected List<BeginNode> blocks;
+    protected List<BeginNode> earlyExits;
+    protected LoopBeginNode loop;
+    protected Map<Node, Node> duplicationMapping;
+    protected SuperBlock original;
+    protected NodeBitMap loopNodes;
+
+    public SuperBlock(BeginNode entry, BeginNode exit, List<BeginNode> blocks, List<BeginNode> earlyExits, LoopBeginNode loop) {
+        this.entry = entry;
+        this.exit = exit;
+        this.blocks = blocks;
+        this.earlyExits = earlyExits;
+        this.loop = loop;
+        assert blocks.contains(entry);
+        assert !blocks.contains(exit) || exit == entry;
+    }
+
+    public Map<Node, Node> getDuplicationMapping() {
+        return duplicationMapping;
+    }
+
+    public BeginNode getEntry() {
+        return entry;
+    }
+
+    public NodeBitMap loopNodes() {
+        if (loopNodes == null) {
+            loopNodes = computeNodes();
+        }
+        return loopNodes;
+    }
+
+    public SuperBlock duplicate() {
+        NodeBitMap nodes = loopNodes();
+        Map<Node, Node> replacements = new HashMap<>();
+        StructuredGraph graph = (StructuredGraph) entry.graph();
+        BeginNode newEntry = graph.add(new BeginNode());
+        BeginNode newExit = null;
+        List<BeginNode> newEarlyExits = new ArrayList<>(earlyExits.size());
+        if (!(exit instanceof MergeNode)) {
+            newExit = graph.add(new BeginNode());
+            replacements.put(exit, newExit);
+        }
+        replacements.put(entry, newEntry); // no merge/loop begin
+        for (BeginNode earlyExit : earlyExits) {
+            BeginNode newEarlyExit = graph.add(new BeginNode());
+            newEarlyExits.add(newEarlyExit);
+            replacements.put(earlyExit, newEarlyExit);
+        }
+        if (loop != null) {
+            for (LoopEndNode end : loop.loopEnds()) {
+                if (nodes.isMarked(end)) {
+                    replacements.put(end, graph.add(new EndNode()));
+                }
+            }
+        }
+        Map<Node, Node> duplicates = graph.addDuplicates(nodes, replacements);
+        if (exit instanceof MergeNode) {
+            newExit = mergeExits(replacements, graph, duplicates, (MergeNode) exit);
+        }
+
+        List<BeginNode> newBlocks = new ArrayList<>(blocks.size());
+        for (BeginNode block : blocks) {
+            BeginNode newBlock = (BeginNode) duplicates.get(block);
+            if (newBlock == null) {
+                newBlock = (BeginNode) replacements.get(block);
+            }
+            assert newBlock != null : block;
+            newBlocks.add(newBlock);
+        }
+        for (Entry<Node, Node> e : replacements.entrySet()) {
+            duplicates.put(e.getKey(), e.getValue());
+        }
+        SuperBlock superBlock = new SuperBlock(newEntry, newExit, newBlocks, newEarlyExits, loop);
+        superBlock.duplicationMapping = duplicates;
+        superBlock.original = this;
+        return superBlock;
+    }
+
+    private BeginNode mergeExits(Map<Node, Node> replacements, StructuredGraph graph, Map<Node, Node> duplicates, MergeNode mergeExit) {
+        BeginNode newExit;
+        List<EndNode> endsToMerge = new LinkedList<>();
+        if (mergeExit == loop) {
+            LoopBeginNode loopBegin = (LoopBeginNode) mergeExit;
+            for (LoopEndNode le : loopBegin.loopEnds()) {
+                Node duplicate = replacements.get(le);
+                if (duplicate != null) {
+                    endsToMerge.add((EndNode) duplicate);
+                }
+            }
+        } else {
+            for (EndNode end : mergeExit.forwardEnds()) {
+                Node duplicate = duplicates.get(end);
+                if (duplicate != null) {
+                    endsToMerge.add((EndNode) duplicate);
+                }
+            }
+        }
+
+        if (endsToMerge.size() == 1) {
+            EndNode end = endsToMerge.get(0);
+            assert end.usages().count() == 0;
+            newExit = graph.add(new BeginNode());
+            end.replaceAtPredecessors(newExit);
+            end.safeDelete();
+        } else {
+            assert endsToMerge.size() > 1;
+            MergeNode newExitMerge = graph.add(new MergeNode());
+            newExit = newExitMerge;
+            FrameState state = mergeExit.stateAfter().duplicate();
+            newExitMerge.setStateAfter(state); // this state is wrong (incudes phis from the loop begin) needs to be fixed while resolving data
+            for (EndNode end : endsToMerge) {
+                newExitMerge.addForwardEnd(end);
+            }
+        }
+        return newExit;
+    }
+
+    public void finish() {
+        if (original != null) {
+            mergeEarlyExits((StructuredGraph) entry.graph(), original.earlyExits, duplicationMapping);
+        }
+    }
+
+    private static void mergeEarlyExits(StructuredGraph graph, List<BeginNode> earlyExits, Map<Node, Node> duplicates) {
+        for (BeginNode earlyExit : earlyExits) {
+            BeginNode newEarlyExit = (BeginNode) duplicates.get(earlyExit);
+            assert newEarlyExit != null;
+            MergeNode merge = graph.add(new MergeNode());
+            EndNode originalEnd = graph.add(new EndNode());
+            EndNode newEnd = graph.add(new EndNode());
+            merge.addForwardEnd(originalEnd);
+            merge.addForwardEnd(newEnd);
+            FixedNode next = earlyExit.next();
+            earlyExit.setNext(originalEnd);
+            newEarlyExit.setNext(newEnd);
+            merge.setNext(next);
+            FrameState exitState = earlyExit.stateAfter();
+            FrameState state = exitState.duplicate();
+            merge.setStateAfter(state);
+            for (ValueProxyNode vpn : earlyExit.proxies().snapshot()) {
+                ValueNode replaceWith;
+                ValueProxyNode newVpn = (ValueProxyNode) duplicates.get(vpn);
+                if (newVpn != null) {
+                    PhiNode phi = graph.add(new PhiNode(vpn.kind(), merge, vpn.type()));
+                    phi.addInput(vpn);
+                    phi.addInput(newVpn);
+                    if (vpn.type() == PhiType.Value) {
+                        replaceWith = phi;
+                    } else {
+                        assert vpn.type() == PhiType.Virtual;
+                        VirtualObjectFieldNode vof = (VirtualObjectFieldNode) GraphUtil.unProxify(vpn);
+                        VirtualObjectFieldNode newVof = (VirtualObjectFieldNode) GraphUtil.unProxify(newVpn);
+                        replaceWith = mergeVirtualChain(graph, vof, newVof, phi, earlyExit, newEarlyExit, merge);
+                    }
+                } else {
+                    replaceWith = vpn.value();
+                }
+                state.replaceFirstInput(vpn, replaceWith);
+                for (Node usage : vpn.usages().snapshot()) {
+                    if (usage != exitState && !merge.isPhiAtMerge(usage)) {
+                        usage.replaceFirstInput(vpn, replaceWith);
+                    }
+                }
+            }
+        }
+    }
+
+    private static ValueProxyNode findProxy(ValueNode value, BeginNode proxyPoint) {
+        for (ValueProxyNode vpn : proxyPoint.proxies()) {
+            if (GraphUtil.unProxify(vpn) == value) {
+                return vpn;
+            }
+        }
+        return null;
+    }
+
+    private static ValueNode mergeVirtualChain(
+                    StructuredGraph graph,
+                    VirtualObjectFieldNode vof,
+                    VirtualObjectFieldNode newVof,
+                    PhiNode vPhi,
+                    BeginNode earlyExit,
+                    BeginNode newEarlyExit,
+                    MergeNode merge) {
+        VirtualObjectNode vObject = vof.object();
+        assert newVof.object() == vObject;
+        ValueNode[] virtualState = virtualState(vof);
+        ValueNode[] newVirtualState = virtualState(newVof);
+        ValueNode chain = vPhi;
+        for (int i = 0; i < virtualState.length; i++) {
+            ValueNode value = virtualState[i];
+            ValueNode newValue = newVirtualState[i];
+            assert value.kind() == newValue.kind();
+            if (value != newValue) {
+                PhiNode valuePhi = graph.add(new PhiNode(value.kind(), merge, PhiType.Value));
+                ValueProxyNode inputProxy = findProxy(value, earlyExit);
+                if (inputProxy != null) {
+                    ValueProxyNode newInputProxy = findProxy(newValue, newEarlyExit);
+                    assert newInputProxy != null;
+                    valuePhi.addInput(inputProxy);
+                    valuePhi.addInput(newInputProxy);
+                } else {
+                    valuePhi.addInput(graph.unique(new ValueProxyNode(value, earlyExit, PhiType.Value)));
+                    valuePhi.addInput(newValue);
+                }
+                chain = graph.add(new VirtualObjectFieldNode(vObject, chain, valuePhi, i));
+            }
+        }
+        return chain;
+    }
+
+    private static ValueNode[] virtualState(VirtualObjectFieldNode vof) {
+        VirtualObjectNode vObj = vof.object();
+        int fieldsCount = vObj.fieldsCount();
+        int dicovered = 0;
+        ValueNode[] state = new ValueNode[fieldsCount];
+        ValueNode currentField = vof;
+        do {
+            if (currentField instanceof VirtualObjectFieldNode) {
+                int index = ((VirtualObjectFieldNode) currentField).index();
+                if (state[index] == null) {
+                    dicovered++;
+                    state[index] = ((VirtualObjectFieldNode) currentField).input();
+                    if (dicovered >= fieldsCount) {
+                        break;
+                    }
+                }
+                currentField = ((VirtualObjectFieldNode) currentField).lastState();
+            } else {
+                assert currentField instanceof PhiNode && ((PhiNode) currentField).type() == PhiType.Virtual : currentField;
+                currentField = ((PhiNode) currentField).valueAt(0);
+            }
+        } while (currentField != null);
+        return state;
+    }
+
+    private NodeBitMap computeNodes() {
+        NodeBitMap nodes = entry.graph().createNodeBitMap();
+        for (BeginNode b : blocks) {
+            for (Node n : b.getBlockNodes()) {
+                if (n instanceof Invoke) {
+                    nodes.mark(((Invoke) n).callTarget());
+                }
+                if (n instanceof StateSplit) {
+                    FrameState stateAfter = ((StateSplit) n).stateAfter();
+                    if (stateAfter != null) {
+                        nodes.mark(stateAfter);
+                    }
+                }
+                nodes.mark(n);
+            }
+        }
+        for (BeginNode earlyExit : earlyExits) {
+            FrameState stateAfter = earlyExit.stateAfter();
+            assert stateAfter != null;
+            nodes.mark(stateAfter);
+            nodes.mark(earlyExit);
+            for (ValueProxyNode proxy : earlyExit.proxies()) {
+                nodes.mark(proxy);
+            }
+        }
+
+        for (BeginNode b : blocks) {
+            for (Node n : b.getBlockNodes()) {
+                for (Node usage : n.usages()) {
+                    markFloating(usage, nodes, "");
+                }
+            }
+        }
+
+        if (entry instanceof LoopBeginNode) {
+            for (PhiNode phi : ((LoopBeginNode) entry).phis()) {
+                nodes.clear(phi);
+            }
+        }
+
+        return nodes;
+    }
+
+    private static boolean markFloating(Node n, NodeBitMap loopNodes, String ind) {
+        //System.out.println(ind + "markFloating(" + n + ")");
+        if (loopNodes.isMarked(n)) {
+            return true;
+        }
+        if (n instanceof FixedNode) {
+            return false;
+        }
+        boolean mark = false;
+        if (n instanceof PhiNode) {
+            mark = loopNodes.isMarked(((PhiNode) n).merge());
+            if (mark) {
+                loopNodes.mark(n);
+            } else {
+                return false;
+            }
+        }
+        for (Node usage : n.usages()) {
+            if (markFloating(usage, loopNodes, " " + ind)) {
+                mark = true;
+            }
+        }
+        if (mark) {
+            loopNodes.mark(n);
+            return true;
+        }
+        return false;
+    }
+
+    public void insertBefore(FixedNode fixed) {
+        assert entry.predecessor() == null;
+        assert exit.next() == null;
+        fixed.replaceAtPredecessors(entry);
+        exit.setNext(fixed);
+    }
+}
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EscapeAnalysisPhase.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/EscapeAnalysisPhase.java	Tue Apr 10 12:22:46 2012 +0200
@@ -130,8 +130,12 @@
 
         @Override
         public void loopEnds(LoopBeginNode loopBegin, List<BlockExitState> loopEndStates) {
-            while (!(virtualObjectField instanceof PhiNode)) {
-                virtualObjectField = ((VirtualObjectFieldNode) virtualObjectField).lastState();
+            while (!loopBegin.isPhiAtMerge(virtualObjectField)) {
+                if (virtualObjectField instanceof PhiNode) {
+                    virtualObjectField = ((PhiNode) virtualObjectField).valueAt(0);
+                } else {
+                    virtualObjectField = ((VirtualObjectFieldNode) virtualObjectField).lastState();
+                }
             }
             for (BlockExitState loopEndState : loopEndStates) {
                 ((PhiNode) virtualObjectField).addInput(loopEndState.virtualObjectField);
@@ -184,6 +188,11 @@
             FixedNode next = node.next();
             graph.removeFixed(node);
 
+            for (ValueProxyNode vpn : virtual.usages().filter(ValueProxyNode.class).snapshot()) {
+                assert vpn.value() == virtual;
+                graph.replaceFloating(vpn, virtual);
+            }
+
             if (virtual.fieldsCount() > 0) {
                 final BlockExitState startState = new BlockExitState(escapeFields, virtual);
                 final PostOrderNodeIterator<?> iterator = new PostOrderNodeIterator<BlockExitState>(next, startState) {
@@ -193,9 +202,25 @@
                         if (changedField != -1) {
                             state.updateField(changedField);
                         }
+                        if (curNode instanceof LoopExitNode) {
+                            state.virtualObjectField = graph.unique(new ValueProxyNode(state.virtualObjectField, (LoopExitNode) curNode, PhiType.Virtual));
+                            for (int i = 0; i < state.fieldState.length; i++) {
+                                state.fieldState[i] = graph.unique(new ValueProxyNode(state.fieldState[i], (LoopExitNode) curNode, PhiType.Value));
+                            }
+                        }
                         if (!curNode.isDeleted() && curNode instanceof StateSplit && ((StateSplit) curNode).stateAfter() != null) {
                             if (state.virtualObjectField != null) {
-                                ((StateSplit) curNode).stateAfter().addVirtualObjectMapping(state.virtualObjectField);
+                                ValueNode v = state.virtualObjectField;
+                                if (curNode instanceof LoopBeginNode) {
+                                    while (!((LoopBeginNode) curNode).isPhiAtMerge(v)) {
+                                        if (v instanceof PhiNode) {
+                                            v = ((PhiNode) v).valueAt(0);
+                                        } else {
+                                            v = ((VirtualObjectFieldNode) v).lastState();
+                                        }
+                                    }
+                                }
+                                ((StateSplit) curNode).stateAfter().addVirtualObjectMapping(v);
                             }
                         }
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/LoopTransformPhase.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.phases;
+
+import java.util.*;
+
+import com.oracle.graal.compiler.*;
+import com.oracle.graal.compiler.loop.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.cfg.*;
+import com.oracle.graal.nodes.*;
+
+public class LoopTransformPhase extends Phase {
+
+    @Override
+    protected void run(StructuredGraph graph) {
+        if (graph.hasLoops()) {
+            ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false);
+            Loop[] loops = cfg.getLoops();
+            // outermost first
+            Arrays.sort(loops, new Comparator<Loop>() {
+                @Override
+                public int compare(Loop o1, Loop o2) {
+                    return o1.depth - o2.depth;
+                }
+            });
+            for (Loop loop : loops) {
+                double entryProbability = loop.loopBegin().forwardEnd().probability();
+                if (entryProbability > GraalOptions.MinimumPeelProbability) {
+                    Debug.log("Peeling %s", loop);
+                    LoopTransformUtil.peel(loop);
+                    Debug.dump(graph, "After peeling %s", loop);
+                }
+            }
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/RemoveValueProxyPhase.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.phases;
+
+import com.oracle.graal.nodes.*;
+
+public class RemoveValueProxyPhase extends Phase {
+
+    @Override
+    protected void run(StructuredGraph graph) {
+        for (ValueProxyNode vpn : graph.getNodes(ValueProxyNode.class)) {
+            graph.replaceFloating(vpn, vpn.value());
+        }
+        for (LoopExitNode exit : graph.getNodes(LoopExitNode.class)) {
+            FrameState stateAfter = exit.stateAfter();
+            if (stateAfter != null) {
+                exit.setStateAfter(null);
+                if (stateAfter.usages().count() == 0) {
+                    stateAfter.safeDelete();
+                }
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/SnippetIntrinsificationPhase.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/SnippetIntrinsificationPhase.java	Tue Apr 10 12:22:46 2012 +0200
@@ -227,6 +227,9 @@
         if (newInstance instanceof ValueNode && ((ValueNode) newInstance).kind() != CiKind.Object) {
             StructuredGraph graph = (StructuredGraph) newInstance.graph();
             for (CheckCastNode checkCastNode : newInstance.usages().filter(CheckCastNode.class).snapshot()) {
+                for (ValueProxyNode vpn : checkCastNode.usages().filter(ValueProxyNode.class).snapshot()) {
+                    graph.replaceFloating(vpn, checkCastNode);
+                }
                 for (Node checkCastUsage : checkCastNode.usages().snapshot()) {
                     if (checkCastUsage instanceof ValueAnchorNode) {
                         ValueAnchorNode valueAnchorNode = (ValueAnchorNode) checkCastUsage;
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/schedule/SchedulePhase.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/schedule/SchedulePhase.java	Tue Apr 10 12:22:46 2012 +0200
@@ -108,6 +108,7 @@
             return;
         }
         assert !(n instanceof PhiNode) : n;
+        assert !(n instanceof MergeNode);
         // if in CFG, schedule at the latest position possible in the outermost loop possible
         Block latestBlock = latestBlock(n);
         Block block;
@@ -120,7 +121,6 @@
         } else {
             block = latestBlock;
         }
-        assert !(n instanceof MergeNode);
         cfg.getNodeToBlock().set(n, block);
         nodesFor.get(block).add(n);
     }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java	Tue Apr 10 12:22:46 2012 +0200
@@ -882,15 +882,7 @@
             returnDuplicate.clearInputs();
             Node n = invoke.next();
             invoke.setNext(null);
-            if (n instanceof BeginNode) {
-                BeginNode begin = (BeginNode) n;
-                FixedNode next = begin.next();
-                begin.setNext(null);
-                returnDuplicate.replaceAndDelete(next);
-                begin.safeDelete();
-            } else {
-                returnDuplicate.replaceAndDelete(n);
-            }
+            returnDuplicate.replaceAndDelete(n);
         }
 
         invoke.node().clearInputs();
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Tue Apr 10 12:22:46 2012 +0200
@@ -352,6 +352,10 @@
             node.getNodeClass().set(node, this, value);
         }
 
+        public boolean isValidFor(Node node, Node from) {
+            return node.getNodeClass().isValid(this, from.getNodeClass());
+        }
+
         @Override
         public int hashCode() {
             final int prime = 31;
@@ -605,6 +609,18 @@
         return true;
     }
 
+    public boolean isValid(Position pos, NodeClass from) {
+        long[] offsets = pos.input ? inputOffsets : successorOffsets;
+        if (pos.index >= offsets.length) {
+            return false;
+        }
+        long[] fromOffsets = pos.input ? from.inputOffsets : from.successorOffsets;
+        if (pos.index >= fromOffsets.length) {
+            return false;
+        }
+        return offsets[pos.index] == fromOffsets[pos.index];
+    }
+
     public Node get(Node node, Position pos) {
         long offset = pos.input ? inputOffsets[pos.index] : successorOffsets[pos.index];
         if (pos.subIndex == NOT_ITERABLE) {
@@ -678,7 +694,7 @@
         while (index < directInputCount) {
             Node input = getNode(node, inputOffsets[index]);
             if (input == old) {
-                assert other == null || inputTypes[index].isAssignableFrom(other.getClass());
+                assert other == null || inputTypes[index].isAssignableFrom(other.getClass()); // : "Can not assign " + other.getClass() + " to " + inputTypes[index] + " in " + node;
                 putNode(node, inputOffsets[index], other);
                 return true;
             }
@@ -700,7 +716,7 @@
         while (index < directSuccessorCount) {
             Node successor = getNode(node, successorOffsets[index]);
             if (successor == old) {
-                assert other == null || successorTypes[index].isAssignableFrom(other.getClass()) : successorTypes[index] + " is not compatible with " + other.getClass();
+                assert other == null || successorTypes[index].isAssignableFrom(other.getClass()); // : successorTypes[index] + " is not compatible with " + other.getClass();
                 putNode(node, successorOffsets[index], other);
                 return true;
             }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Tue Apr 10 12:22:46 2012 +0200
@@ -132,8 +132,12 @@
         } catch (CiBailout bailout) {
             Debug.metric("Bailouts").increment();
             if (GraalOptions.ExitVMOnBailout) {
+                TTY.cachedOut.println(CiUtil.format("%H.%n(%p)", method));
                 bailout.printStackTrace(TTY.cachedOut);
                 System.exit(-1);
+            } else if (GraalOptions.PrintBailout) {
+                TTY.cachedOut.println(CiUtil.format("%H.%n(%p)", method));
+                bailout.printStackTrace(TTY.cachedOut);
             }
         } catch (Throwable t) {
             if (GraalOptions.ExitVMOnException) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotRuntime.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotRuntime.java	Tue Apr 10 12:22:46 2012 +0200
@@ -304,10 +304,9 @@
                     AnchorNode anchor = graph.add(new AnchorNode());
                     graph.addBeforeFixed(storeIndexed, anchor);
                     GuardNode guard = (GuardNode) tool.createGuard(graph.unique(new NullCheckNode(array, false)), RiDeoptReason.NullCheckException, RiDeoptAction.InvalidateReprofile, StructuredGraph.INVALID_GRAPH_ID);
-                    ReadNode arrayClass = graph.add(new ReadNode(array, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.hubOffset, graph), StampFactory.objectNonNull()));
+                    FloatingReadNode arrayClass = graph.unique(new FloatingReadNode(array, null, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.hubOffset, graph), StampFactory.objectNonNull()));
                     arrayClass.setGuard(guard);
-                    graph.addBeforeFixed(storeIndexed, arrayClass);
-                    ReadNode arrayElementKlass = graph.add(new ReadNode(arrayClass, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.arrayClassElementOffset, graph), StampFactory.objectNonNull()));
+                    FloatingReadNode arrayElementKlass = graph.unique(new FloatingReadNode(arrayClass, null, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Object, config.arrayClassElementOffset, graph), StampFactory.objectNonNull()));
                     value = graph.unique(new CheckCastNode(anchor, arrayElementKlass, null, value));
                 }
             }
@@ -387,7 +386,7 @@
                 SafeReadNode klassOop = safeRead(graph, CiKind.Object, receiver, config.klassOopOffset, StampFactory.objectNonNull(), StructuredGraph.INVALID_GRAPH_ID);
                 graph.start().setNext(klassOop);
                 // TODO(thomaswue): Care about primitive classes! Crashes for primitive classes at the moment (klassOop == null)
-                ReadNode result = graph.add(new ReadNode(klassOop, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Int, config.klassModifierFlagsOffset, graph), StampFactory.forKind(CiKind.Int)));
+                FloatingReadNode result = graph.unique(new FloatingReadNode(klassOop, null, LocationNode.create(LocationNode.FINAL_LOCATION, CiKind.Int, config.klassModifierFlagsOffset, graph), StampFactory.forKind(CiKind.Int)));
                 ReturnNode ret = graph.add(new ReturnNode(result));
                 klassOop.setNext(ret);
                 return graph;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Tue Apr 10 12:22:46 2012 +0200
@@ -121,12 +121,14 @@
         public int endBci;
         public boolean isExceptionEntry;
         public boolean isLoopHeader;
+        public int loopId;
         public int blockID;
 
         public FixedWithNextNode firstInstruction;
         public FrameStateBuilder entryState;
 
         public ArrayList<Block> successors = new ArrayList<>(2);
+        public long exits;
         public int normalSuccessors;
 
         private boolean visited;
@@ -191,6 +193,7 @@
     private final BytecodeStream stream;
     private final RiExceptionHandler[] exceptionHandlers;
     private Block[] blockMap;
+    public Block[] loopHeaders;
 
     /**
      * Creates a new BlockMap instance from bytecode of the given method .
@@ -204,6 +207,7 @@
         this.blockMap = new Block[method.codeSize()];
         this.canTrap = new BitSet(blockMap.length);
         this.blocks = new ArrayList<>();
+        this.loopHeaders = new Block[64];
     }
 
     public RiExceptionHandler[] exceptionHandlers() {
@@ -223,7 +227,11 @@
             }
             createJsrAlternatives(blockMap[0]);
         }
+        if (Debug.isLogEnabled()) {
+            this.log("Before BlockOrder");
+        }
         computeBlockOrder();
+        fixLoopBits();
 
         initializeBlockIds();
 
@@ -231,7 +239,9 @@
 
         // Discard big arrays so that they can be GCed
         blockMap = null;
-
+        if (Debug.isLogEnabled()) {
+            this.log("Before LivenessAnalysis");
+        }
         if (GraalOptions.OptLivenessAnalysis) {
             Debug.scope("LivenessAnalysis", new Runnable() {
                 @Override
@@ -541,6 +551,26 @@
         }
     }
 
+    private boolean loopChanges;
+
+    private void fixLoopBits() {
+        do {
+            loopChanges = false;
+            for (Block b : blocks) {
+                b.visited = false;
+            }
+
+            long loop = fixLoopBits(blockMap[0]);
+
+            if (loop != 0) {
+                // There is a path from a loop end to the method entry that does not pass the loop header.
+                // Therefore, the loop is non reducible (has more than one entry).
+                // We don't want to compile such methods because the IR only supports structured loops.
+                throw new CiBailout("Non-reducible loop");
+            }
+        } while (loopChanges);
+    }
+
     private void computeBlockOrder() {
         long loop = computeBlockOrder(blockMap[0]);
 
@@ -555,6 +585,64 @@
         Collections.reverse(blocks);
     }
 
+    public void log(String name) {
+        if (Debug.isLogEnabled()) {
+            String n = System.lineSeparator();
+            StringBuilder sb = new StringBuilder(Debug.currentScope()).append("BlockMap ").append(name).append(" :");
+            sb.append(n);
+            Iterable<Block> it;
+            if (blocks.isEmpty()) {
+                it = new HashSet<>(Arrays.asList(blockMap));
+            } else {
+                it = blocks;
+            }
+            for (Block b : it) {
+                if (b == null) {
+                    continue;
+                }
+                sb.append("B").append(b.blockID).append(" (").append(b.startBci).append(" -> ").append(b.endBci).append(")");
+                if (b.isLoopHeader) {
+                    sb.append(" LoopHeader");
+                }
+                if (b.isExceptionEntry) {
+                    sb.append(" ExceptionEntry");
+                }
+                sb.append(n).append("  Sux : ");
+                for (Block s : b.successors) {
+                    sb.append("B").append(s.blockID).append(" (").append(s.startBci).append(" -> ").append(s.endBci).append(")");
+                    if (s.isExceptionEntry) {
+                        sb.append("!");
+                    }
+                    sb.append(" ");
+                }
+                sb.append(n).append("  Loop : ");
+                long l = b.loops;
+                int pos = 0;
+                while (l != 0) {
+                    int lMask = 1 << pos;
+                    if ((l & lMask) != 0) {
+                        sb.append("B").append(loopHeaders[pos].blockID).append(" ");
+                        l &= ~lMask;
+                    }
+                    pos++;
+                }
+                sb.append(n).append("  Exits : ");
+                l = b.exits;
+                pos = 0;
+                while (l != 0) {
+                    int lMask = 1 << pos;
+                    if ((l & lMask) != 0) {
+                        sb.append("B").append(loopHeaders[pos].blockID).append(" ");
+                        l &= ~lMask;
+                    }
+                    pos++;
+                }
+                sb.append(n);
+            }
+            Debug.log(sb.toString());
+        }
+    }
+
     /**
      * The next available loop number.
      */
@@ -581,6 +669,9 @@
 
             assert block.loops == 0;
             block.loops = (long) 1 << (long) nextLoop;
+            Debug.log("makeLoopHeader(%s) -> %x", block, block.loops);
+            loopHeaders[nextLoop] = block;
+            block.loopId = nextLoop;
             nextLoop++;
         }
         assert Long.bitCount(block.loops) == 1;
@@ -596,9 +687,13 @@
             if (block.active) {
                 // Reached block via backward branch.
                 makeLoopHeader(block);
+                // Return cached loop information for this block.
+                return block.loops;
+            } else if (block.isLoopHeader) {
+                return block.loops & ~(1L << block.loopId);
+            } else {
+                return block.loops;
             }
-            // Return cached loop information for this block.
-            return block.loops;
         }
 
         block.visited = true;
@@ -610,18 +705,50 @@
             loops |= computeBlockOrder(successor);
         }
 
+        block.loops = loops;
+        Debug.log("computeBlockOrder(%s) -> %x", block, block.loops);
+
         if (block.isLoopHeader) {
-            assert Long.bitCount(block.loops) == 1;
-            loops &= ~block.loops;
+            loops &= ~(1L << block.loopId);
         }
 
-        block.loops = loops;
         block.active = false;
         blocks.add(block);
 
         return loops;
     }
 
+    private long fixLoopBits(Block block) {
+        if (block.visited) {
+            // Return cached loop information for this block.
+            if (block.isLoopHeader) {
+                return block.loops & ~(1L << block.loopId);
+            } else {
+                return block.loops;
+            }
+        }
+
+        block.visited = true;
+        long loops = block.loops;
+        for (Block successor : block.successors) {
+            // Recursively process successors.
+            loops |= fixLoopBits(successor);
+        }
+        for (Block successor : block.successors) {
+            successor.exits = loops & ~successor.loops;
+        }
+        if (block.loops != loops) {
+            loopChanges = true;
+            block.loops = loops;
+            Debug.log("fixLoopBits0(%s) -> %x", block, block.loops);
+        }
+
+        if (block.isLoopHeader) {
+            loops &= ~(1L << block.loopId);
+        }
+
+        return loops;
+    }
 
     private void computeLiveness() {
         for (Block block : blocks) {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Tue Apr 10 12:22:46 2012 +0200
@@ -28,6 +28,7 @@
 
 import java.util.*;
 
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.nodes.*;
@@ -200,15 +201,40 @@
         }
         // Collect all phi functions that use this phi so that we can delete them recursively (after we delete ourselfs to avoid circles).
         List<PhiNode> phiUsages = phi.usages().filter(PhiNode.class).snapshot();
+        List<ValueProxyNode> vpnUsages = phi.usages().filter(ValueProxyNode.class).snapshot();
 
         // Remove the phi function from all FrameStates where it is used and then delete it.
-        assert phi.usages().filter(isNotA(FrameState.class).nor(PhiNode.class)).isEmpty() : "phi function that gets deletes must only be used in frame states";
+        assert phi.usages().filter(isNotA(FrameState.class).nor(PhiNode.class).nor(ValueProxyNode.class)).isEmpty() : "phi function that gets deletes must only be used in frame states";
         phi.replaceAtUsages(null);
         phi.safeDelete();
 
         for (PhiNode phiUsage : phiUsages) {
             deletePhi(phiUsage);
         }
+        for (ValueProxyNode proxyUsage : vpnUsages) {
+            deleteProxy(proxyUsage);
+        }
+    }
+
+    private void deleteProxy(ValueProxyNode proxy) {
+        if (proxy.isDeleted()) {
+            return;
+        }
+        // Collect all phi functions that use this phi so that we can delete them recursively (after we delete ourselfs to avoid circles).
+        List<PhiNode> phiUsages = proxy.usages().filter(PhiNode.class).snapshot();
+        List<ValueProxyNode> vpnUsages = proxy.usages().filter(ValueProxyNode.class).snapshot();
+
+        // Remove the proxy function from all FrameStates where it is used and then delete it.
+        assert proxy.usages().filter(isNotA(FrameState.class).nor(PhiNode.class).nor(ValueProxyNode.class)).isEmpty() : "phi function that gets deletes must only be used in frame states";
+        proxy.replaceAtUsages(null);
+        proxy.safeDelete();
+
+        for (PhiNode phiUsage : phiUsages) {
+            deletePhi(phiUsage);
+        }
+        for (ValueProxyNode proxyUsage : vpnUsages) {
+            deleteProxy(proxyUsage);
+        }
     }
 
     public void insertLoopPhis(LoopBeginNode loopBegin) {
@@ -220,6 +246,23 @@
         }
     }
 
+    public void insertProxies(LoopExitNode loopExit, FrameStateBuilder loopEntryState) {
+        for (int i = 0; i < localsSize(); i++) {
+            ValueNode value = localAt(i);
+            if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
+                Debug.log(" inserting proxy for %s", value);
+                storeLocal(i, graph.unique(new ValueProxyNode(value, loopExit, PhiType.Value)));
+            }
+        }
+        for (int i = 0; i < stackSize(); i++) {
+            ValueNode value = stackAt(i);
+            if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
+                Debug.log(" inserting proxy for %s", value);
+                storeStack(i, graph.unique(new ValueProxyNode(value, loopExit, PhiType.Value)));
+            }
+        }
+    }
+
     private PhiNode createLoopPhi(MergeNode block, ValueNode value) {
         if (value == null) {
             return null;
@@ -545,4 +588,18 @@
         assert kind != CiKind.Void && kind != CiKind.Illegal;
         return kind == CiKind.Long || kind == CiKind.Double;
     }
+
+    public boolean contains(ValueNode value) {
+        for (int i = 0; i < localsSize(); i++) {
+            if (localAt(i) == value) {
+                return true;
+            }
+        }
+        for (int i = 0; i < stackSize(); i++) {
+            if (stackAt(i) == value) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Apr 10 12:22:46 2012 +0200
@@ -43,6 +43,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
 import com.oracle.max.cri.ci.*;
 import com.oracle.max.cri.ri.*;
 import com.oracle.max.cri.ri.RiType.Representation;
@@ -104,6 +105,8 @@
         }
     }
 
+    private Block[] loopHeaders;
+
     public GraphBuilderPhase(RiRuntime runtime, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) {
         this.graphBuilderConfig = graphBuilderConfig;
         this.optimisticOpts = optimisticOpts;
@@ -156,6 +159,7 @@
         this.canTrapBitSet = blockMap.canTrap;
 
         exceptionHandlers = blockMap.exceptionHandlers();
+        loopHeaders = blockMap.loopHeaders;
 
         lastInstr = currentGraph.start();
         if (isSynchronized(method.accessFlags())) {
@@ -1197,6 +1201,67 @@
         return x;
     }
 
+    private static class Target {
+        FixedNode fixed;
+        FrameStateBuilder state;
+        public Target(FixedNode fixed, FrameStateBuilder state) {
+            this.fixed = fixed;
+            this.state = state;
+        }
+    }
+
+    private Target checkLoopExit(FixedNode traget, Block targetBlock, FrameStateBuilder state) {
+        if (currentBlock != null) {
+            long exits = currentBlock.loops & ~targetBlock.loops;
+            if (exits != 0) {
+                LoopExitNode firstLoopExit = null;
+                LoopExitNode lastLoopExit = null;
+
+                int pos = 0;
+                ArrayList<Block> exitLoops = new ArrayList<>(Long.bitCount(exits));
+                do {
+                    int lMask = 1 << pos;
+                    if ((exits & lMask) != 0) {
+                        exitLoops.add(loopHeaders[pos]);
+                        exits &= ~lMask;
+                    }
+                    pos++;
+                } while (exits != 0);
+
+                Collections.sort(exitLoops, new Comparator<Block>() {
+                    @Override
+                    public int compare(Block o1, Block o2) {
+                        return Long.bitCount(o2.loops) - Long.bitCount(o1.loops);
+                    }
+                });
+
+                int bci = targetBlock.startBci;
+                if (targetBlock instanceof ExceptionBlock) {
+                    bci = ((ExceptionBlock) targetBlock).deoptBci;
+                }
+                FrameStateBuilder newState = state.copy();
+                for (Block loop : exitLoops) {
+                    LoopBeginNode loopBegin = (LoopBeginNode) loop.firstInstruction;
+                    LoopExitNode loopExit = currentGraph.add(new LoopExitNode(loopBegin));
+                    if (lastLoopExit != null) {
+                        lastLoopExit.setNext(loopExit);
+                    }
+                    if (firstLoopExit == null) {
+                        firstLoopExit = loopExit;
+                    }
+                    lastLoopExit = loopExit;
+                    Debug.log("Traget %s (%s) Exits %s, scanning framestates...", targetBlock, traget, loop);
+                    newState.insertProxies(loopExit, loop.entryState);
+                    loopExit.setStateAfter(newState.create(bci));
+                }
+
+                lastLoopExit.setNext(traget);
+                return new Target(firstLoopExit, newState);
+            }
+        }
+        return new Target(traget, state);
+    }
+
     private FixedNode createTarget(double probability, Block block, FrameStateBuilder stateAfter) {
         assert probability >= 0 && probability <= 1;
         if (probability == 0 && optimisticOpts.removeNeverExecutedCode()) {
@@ -1206,23 +1271,25 @@
         }
     }
 
-    private FixedNode createTarget(Block block, FrameStateBuilder stateAfter) {
-        assert block != null && stateAfter != null;
-        assert !block.isExceptionEntry || stateAfter.stackSize() == 1;
+    private FixedNode createTarget(Block block, FrameStateBuilder state) {
+        assert block != null && state != null;
+        assert !block.isExceptionEntry || state.stackSize() == 1;
 
         if (block.firstInstruction == null) {
             // This is the first time we see this block as a branch target.
             // Create and return a placeholder that later can be replaced with a MergeNode when we see this block again.
             block.firstInstruction = currentGraph.add(new BlockPlaceholderNode());
-            block.entryState = stateAfter.copy();
+            Target target = checkLoopExit(block.firstInstruction, block, state);
+            FixedNode result = target.fixed;
+            block.entryState = target.state == state ? state.copy() : target.state;
             block.entryState.clearNonLiveLocals(block.localsLiveIn);
 
             Debug.log("createTarget %s: first visit, result: %s", block, block.firstInstruction);
-            return block.firstInstruction;
+            return result;
         }
 
         // We already saw this block before, so we have to merge states.
-        if (!block.entryState.isCompatibleWith(stateAfter)) {
+        if (!block.entryState.isCompatibleWith(state)) {
             throw new CiBailout("stacks do not match; bytecodes would not verify");
         }
 
@@ -1230,8 +1297,9 @@
             assert block.isLoopHeader && currentBlock.blockID >= block.blockID : "must be backward branch";
             // Backward loop edge. We need to create a special LoopEndNode and merge with the loop begin node created before.
             LoopBeginNode loopBegin = (LoopBeginNode) block.firstInstruction;
-            LoopEndNode result = currentGraph.add(new LoopEndNode(loopBegin));
-            block.entryState.merge(loopBegin, stateAfter);
+            Target target = checkLoopExit(currentGraph.add(new LoopEndNode(loopBegin)), block, state);
+            FixedNode result = target.fixed;
+            block.entryState.merge(loopBegin, target.state);
 
             Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result);
             return result;
@@ -1260,9 +1328,11 @@
         MergeNode mergeNode = (MergeNode) block.firstInstruction;
 
         // The EndNode for the newly merged edge.
-        EndNode result = currentGraph.add(new EndNode());
-        block.entryState.merge(mergeNode, stateAfter);
-        mergeNode.addForwardEnd(result);
+        EndNode newEnd = currentGraph.add(new EndNode());
+        Target target = checkLoopExit(newEnd, block, state);
+        FixedNode result = target.fixed;
+        block.entryState.merge(mergeNode, target.state);
+        mergeNode.addForwardEnd(newEnd);
 
         Debug.log("createTarget %s: merging state, result: %s", block, result);
         return result;
@@ -1274,9 +1344,7 @@
      */
     private BeginNode createBlockTarget(double probability, Block block, FrameStateBuilder stateAfter) {
         FixedNode target = createTarget(probability, block, stateAfter);
-        assert !(target instanceof BeginNode);
-        BeginNode begin = currentGraph.add(new BeginNode());
-        begin.setNext(target);
+        BeginNode begin = BeginNode.begin(target);
 
         assert !(target instanceof DeoptimizeNode && begin.stateAfter() != null) :
             "We are not allowed to set the stateAfter of the begin node, because we have to deoptimize to a bci _before_ the actual if, so that the interpreter can update the profiling information.";
@@ -1340,25 +1408,7 @@
                 assert begin.forwardEndCount() == 1;
                 currentGraph.reduceDegenerateLoopBegin(begin);
             } else {
-                // Delete unnecessary loop phi functions, i.e., phi functions where all inputs are either the same or the phi itself.
-                for (PhiNode phi : begin.phis().snapshot()) {
-                    checkRedundantPhi(phi);
-                }
-            }
-        }
-    }
-
-    private static void checkRedundantPhi(PhiNode phiNode) {
-        if (phiNode.isDeleted() || phiNode.valueCount() == 1) {
-            return;
-        }
-
-        ValueNode singleValue = phiNode.singleValue();
-        if (singleValue != null) {
-            Collection<PhiNode> phiUsages = phiNode.usages().filter(PhiNode.class).snapshot();
-            ((StructuredGraph) phiNode.graph()).replaceFloating(phiNode, singleValue);
-            for (PhiNode phi : phiUsages) {
-                checkRedundantPhi(phi);
+                GraphUtil.normalizeLoopBegin(begin);
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/Loop15.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012, 2012, 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.jtt.loop;
+
+import org.junit.*;
+
+public class Loop15 {
+
+    public static int test(int arg) {
+        Object o = null;
+        int result = 10;
+        for (int k = 0; k < arg; ++k) {
+            if (o == null) {
+                o = new Object();
+            }
+            if (k >= 5) {
+                break;
+            }
+            result++;
+        }
+        return result + (o == null ? 0 : 1);
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        Assert.assertEquals(16, test(5));
+    }
+
+    @Test
+    public void run1() throws Throwable {
+        Assert.assertEquals(10, test(0));
+    }
+
+    @Test
+    public void run2() throws Throwable {
+        Assert.assertEquals(12, test(1));
+    }
+
+    @Test
+    public void run3() throws Throwable {
+        Assert.assertEquals(16, test(10));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/Loop16.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2007, 2012, 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.
+ */
+// Checkstyle: stop
+package com.oracle.graal.jtt.loop;
+
+import org.junit.*;
+
+/*
+ * Tests exiting 2 loops at the same time with escape-analysed values flowing out of loops
+ */
+public class Loop16 {
+
+    public int a;
+    public int b;
+    public int c;
+
+    public static int test(int count) {
+        return new Loop16().run(count);
+    }
+
+    public int run(int count) {
+    l1: for (int i = 0; i <= count; i++) {
+            if (i > 5) {
+                for (int j = 0; j < i; j++) {
+                    a += i;
+                    if (a > 500) {
+                        break l1;
+                    }
+                }
+            } else if (i > 7) {
+                b += i;
+            } else {
+                    c += i;
+            }
+        }
+        return a + b + c;
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        Assert.assertEquals(526, test(40));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/Loop17.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2007, 2012, 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.
+ */
+// Checkstyle: stop
+package com.oracle.graal.jtt.loop;
+
+import org.junit.*;
+
+/*
+ * Test around an object that escapes directly from inside a loop (no virtual phi on the loop)
+ */
+public class Loop17 {
+
+    private static class L {
+        public int a;
+        public int b;
+        public int c;
+        public L(int a, int b, int c) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+    }
+
+
+    public static int test(int count) {
+        int i = 0;
+        L l;
+        do {
+            l = new L(i, i+1, i+2);
+        } while (++i < count);
+
+        return l.a + l.b * 10 + l.c * 100;
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        Assert.assertEquals(543, test(new L(4,4,4).a));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/LoopLastIndexOf.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2012, 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.jtt.loop;
+
+import org.junit.*;
+
+/*
+ * see java.lang.String.lastIndexOf(char[], int, int, char[], int ,int, int)
+ */
+public class LoopLastIndexOf {
+
+    private final char[] v1 = new char[]{'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'};
+    private final char[] v2 = new char[]{'d', 'a'};
+    private final char[] v3 = new char[]{'d', 'b', 'c'};
+    private final char[] v4 = new char[]{'z', 'a', 'b', 'c'};
+
+    public static int test(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) {
+        int rightIndex = sourceCount - targetCount;
+        if (fromIndex < 0) {
+            return -1;
+        }
+        if (fromIndex > rightIndex) {
+            fromIndex = rightIndex;
+        }
+        /* Empty string always matches. */
+        if (targetCount == 0) {
+            return fromIndex;
+        }
+
+        int strLastIndex = targetOffset + targetCount - 1;
+        char strLastChar = target[strLastIndex];
+        int min = sourceOffset + targetCount - 1;
+        int i = min + fromIndex;
+
+        startSearchForLastChar: while (true) {
+            while (i >= min && source[i] != strLastChar) {
+                i--;
+            }
+            if (i < min) {
+                return -1;
+            }
+            int j = i - 1;
+            int start = j - (targetCount - 1);
+            int k = strLastIndex - 1;
+
+            while (j > start) {
+                if (source[j--] != target[k--]) {
+                    i--;
+                    continue startSearchForLastChar;
+                }
+            }
+            return start - sourceOffset + 1;
+        }
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        Assert.assertEquals(7, test(v1, 0, v1.length, v2, 0, v2.length, 10));
+    }
+
+    @Test
+    public void run1() throws Throwable {
+        Assert.assertEquals(-1, test(v1, 0, v1.length, v3, 0, v3.length, 10));
+    }
+
+    @Test
+    public void run2() throws Throwable {
+        Assert.assertEquals(-1, test(v1, 0, v1.length, v4, 0, v4.length, 10));
+    }
+
+    @Test
+    public void run3() throws Throwable {
+        Assert.assertEquals(-1, test(v1, 1, v1.length - 1, v3, 0, v3.length, 10));
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void run4() throws Throwable {
+        Assert.assertEquals(-1, test(v1, 1, v1.length, v3, 0, v3.length, 10));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/LoopParseLong.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 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.jtt.loop;
+
+import org.junit.*;
+
+public class LoopParseLong {
+
+    public static long test(String s, int radix) throws NumberFormatException {
+        if (s == null) {
+            throw new NumberFormatException("null");
+        }
+        if (radix < Character.MIN_RADIX) {
+            throw new NumberFormatException("radix " + radix + " less than Character.MIN_RADIX");
+        }
+        if (radix > Character.MAX_RADIX) {
+            throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX");
+        }
+        long result = 0;
+        boolean negative = false;
+        int i = 0;
+        int len = s.length();
+        long limit = -Long.MAX_VALUE;
+        long multmin;
+        int digit;
+        if (len > 0) {
+            char firstChar = s.charAt(0);
+            if (firstChar < '0') {
+                if (firstChar == '-') {
+                    negative = true;
+                    limit = Long.MIN_VALUE;
+                } else if (firstChar != '+') {
+                    throw new NumberFormatException();
+                }
+                if (len == 1) {
+                    throw new NumberFormatException();
+                }
+                i++;
+            }
+            multmin = limit / radix;
+            while (i < len) {
+                digit = Character.digit(s.charAt(i++), radix);
+                if (digit < 0) {
+                    throw new NumberFormatException();
+                }
+                if (result < multmin) {
+                    throw new NumberFormatException();
+                }
+                result *= radix;
+                if (result < limit + digit) {
+                    throw new NumberFormatException();
+                }
+                result -= digit;
+            }
+        } else {
+            throw new NumberFormatException();
+        }
+        return negative ? result : -result;
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        Assert.assertEquals(Character.digit('7', 10), test("7", 10));
+        Assert.assertEquals(-100, test("-100", 10));
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/cfg/CFGVerifier.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/cfg/CFGVerifier.java	Tue Apr 10 12:22:46 2012 +0200
@@ -46,7 +46,7 @@
                 assert dominated.getDominator() == block;
             }
 
-            assert cfg.getLoops() == null || !block.isLoopHeader() || block.getLoop().header == block;
+            assert cfg.getLoops() == null || !block.isLoopHeader() || block.getLoop().header == block : block.beginNode;
         }
 
         if (cfg.getLoops() != null) {
@@ -58,8 +58,16 @@
 
                     Loop blockLoop = block.getLoop();
                     while (blockLoop != loop) {
+                        assert blockLoop != null;
                         blockLoop = blockLoop.parent;
-                        assert blockLoop != null;
+                    }
+
+                    if (!(block.isLoopHeader() && block.getLoop() == loop)) {
+                        for (Block pred : block.getPredecessors()) {
+                            if (!loop.blocks.contains(pred)) {
+                                return false;
+                            }
+                        }
                     }
                 }
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/cfg/ControlFlowGraph.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/cfg/ControlFlowGraph.java	Tue Apr 10 12:22:46 2012 +0200
@@ -24,6 +24,7 @@
 
 import java.util.*;
 
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 
@@ -201,26 +202,51 @@
                 Loop loop = new Loop(block.getLoop(), loopsList.size(), block);
                 loopsList.add(loop);
 
-                for (LoopEndNode end : ((LoopBeginNode) beginNode).loopEnds()) {
+                LoopBeginNode loopBegin = (LoopBeginNode) beginNode;
+                for (LoopEndNode end : loopBegin.loopEnds()) {
                     Block endBlock = nodeToBlock.get(end);
                     computeLoopBlocks(endBlock, loop);
                 }
+
+                for (LoopExitNode exit : loopBegin.loopExits()) {
+                    Block exitBlock = nodeToBlock.get(exit);
+                    List<Block> predecessors = exitBlock.getPredecessors();
+                    assert predecessors.size() == 1;
+                    computeLoopBlocks(predecessors.get(0), loop);
+                    loop.exits.add(exitBlock);
+                }
+                List<Block> unexpected = new LinkedList<>();
+                for (Block b : loop.blocks) {
+                    for (Block sux : b.getSuccessors()) {
+                        if (sux.loop != loop) {
+                            BeginNode begin = sux.getBeginNode();
+                            if (!(begin instanceof LoopExitNode && ((LoopExitNode) begin).loopBegin() == loopBegin)) {
+                                Debug.log("Unexpected loop exit with %s, including whole branch in the loop", sux);
+                                unexpected.add(sux);
+                            }
+                        }
+                    }
+                }
+                for (Block b : unexpected) {
+                    addBranchToLoop(loop, b);
+                }
             }
         }
         loops = loopsList.toArray(new Loop[loopsList.size()]);
+    }
 
-        for (Loop loop : loops) {
-            for (Block block : loop.blocks) {
-                for (Block sux : block.getSuccessors()) {
-                    if (sux.getLoopDepth() < loop.depth) {
-                        loop.exits.add(sux);
-                    }
-                }
-            }
+    private static void addBranchToLoop(Loop l, Block b) {
+        if (l.blocks.contains(b)) {
+            return;
+        }
+        l.blocks.add(b);
+        b.loop = l;
+        for (Block sux : b.getSuccessors()) {
+            addBranchToLoop(l, sux);
         }
     }
 
-    private void computeLoopBlocks(Block block, Loop loop) {
+    private static void computeLoopBlocks(Block block, Loop loop) {
         if (block.getLoop() == loop) {
             return;
         }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/cfg/Loop.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/cfg/Loop.java	Tue Apr 10 12:22:46 2012 +0200
@@ -24,6 +24,8 @@
 
 import java.util.*;
 
+import com.oracle.graal.nodes.*;
+
 public class Loop {
     public final Loop parent;
     public final List<Loop> children;
@@ -53,4 +55,8 @@
     public String toString() {
         return "loop " + index + " depth " + depth + (parent != null ? " outer " + parent.index : "");
     }
+
+    public LoopBeginNode loopBegin() {
+        return (LoopBeginNode) header.getBeginNode();
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/BeginNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.nodes;
 
+import static com.oracle.graal.graph.iterators.NodePredicates.*;
+
 import java.util.*;
 
 import com.oracle.graal.graph.*;
@@ -59,28 +61,45 @@
             // This begin node is necessary.
         } else {
             // This begin node can be removed and all guards moved up to the preceding begin node.
-            if (!usages().isEmpty()) {
-                Node prevBegin = prev;
-                while (!(prevBegin instanceof BeginNode)) {
-                    prevBegin = prevBegin.predecessor();
-                }
-                for (Node usage : usages()) {
-                    tool.addToWorkList(usage);
-                }
-                replaceAtUsages(prevBegin);
-            }
+            prepareDelete();
             ((StructuredGraph) graph()).removeFixed(this);
         }
     }
 
-    public void evacuateGuards() {
+    public static BeginNode prevBegin(FixedNode from) {
+        Node prevBegin = from;
+        while (prevBegin != null) {
+            if (prevBegin instanceof BeginNode) {
+                return (BeginNode) prevBegin;
+            }
+            prevBegin = prevBegin.predecessor();
+        }
+        return null;
+    }
+
+    public void evacuateGuards(FixedNode evacuateFrom) {
         if (!usages().isEmpty()) {
-            Node prevBegin = predecessor();
+            BeginNode prevBegin = prevBegin(evacuateFrom);
             assert prevBegin != null;
-            while (!(prevBegin instanceof BeginNode)) {
-                prevBegin = prevBegin.predecessor();
+            for (Node anchored : anchored().snapshot()) {
+                anchored.replaceFirstInput(this, prevBegin);
             }
-            replaceAtUsages(prevBegin);
+        }
+    }
+
+    public void prepareDelete() {
+        prepareDelete((FixedNode) predecessor());
+    }
+
+    public void prepareDelete(FixedNode evacuateFrom) {
+        removeProxies();
+        evacuateGuards(evacuateFrom);
+    }
+
+    public void removeProxies() {
+        StructuredGraph graph = (StructuredGraph) graph();
+        for (ValueProxyNode vpn : proxies().snapshot()) {
+            graph.replaceFloating(vpn, vpn.value());
         }
     }
 
@@ -98,4 +117,53 @@
     public NodeIterable<GuardNode> guards() {
         return usages().filter(GuardNode.class);
     }
+
+    public NodeIterable<Node> anchored() {
+        return usages().filter(isNotA(ValueProxyNode.class));
+    }
+
+    public NodeIterable<ValueProxyNode> proxies() {
+        return usages().filter(ValueProxyNode.class);
+    }
+
+    public NodeIterable<FixedNode> getBlockNodes() {
+        return new NodeIterable<FixedNode>() {
+            @Override
+            public Iterator<FixedNode> iterator() {
+                return new BlockNodeIterator(BeginNode.this);
+            }
+        };
+    }
+
+    private class BlockNodeIterator implements Iterator<FixedNode> {
+        private FixedNode current;
+
+        public BlockNodeIterator(FixedNode next) {
+            this.current = next;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return current != null;
+        }
+
+        @Override
+        public FixedNode next() {
+            FixedNode ret = current;
+            if (ret == null) {
+                throw new NoSuchElementException();
+            }
+            if (!(current instanceof FixedWithNextNode) || (current instanceof BeginNode && current != BeginNode.this)) {
+                current = null;
+            } else {
+                current = ((FixedWithNextNode) current).next();
+            }
+            return ret;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FixedNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FixedNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -53,4 +53,9 @@
         return properties;
     }
 
+    @Override
+    public boolean verify() {
+        assertTrue(this.successors().isNotEmpty() || this.predecessor() != null, "FixedNode should not float");
+        return super.verify();
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue Apr 10 12:22:46 2012 +0200
@@ -161,7 +161,7 @@
     }
 
     public void addVirtualObjectMapping(Node virtualObject) {
-        assert virtualObject instanceof VirtualObjectFieldNode || virtualObject instanceof PhiNode : virtualObject;
+        assert virtualObject instanceof VirtualObjectFieldNode || virtualObject instanceof PhiNode || virtualObject instanceof ValueProxyNode : virtualObject;
         virtualObjectMappings.add(virtualObject);
     }
 
@@ -184,6 +184,13 @@
         return duplicate(newBci, false);
     }
 
+    /**
+     * Gets a copy of this frame state.
+     */
+    public FrameState duplicate() {
+        return duplicate(bci);
+    }
+
     public FrameState duplicate(int newBci, boolean duplicateOuter) {
         FrameState other = graph().add(new FrameState(method, newBci, localsSize, stackSize, rethrowException, duringCall));
         other.values.setAll(values);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -161,14 +161,16 @@
         EndNode falseEnd = (EndNode) falseSuccessor.next();
         assert trueEnd.merge() == falseEnd.merge();
 
+        FixedWithNextNode pred = (FixedWithNextNode) predecessor();
         MergeNode merge = trueEnd.merge();
+        merge.prepareDelete(pred);
         assert merge.usages().isEmpty();
 
         FixedNode next = merge.next();
         merge.setNext(null);
         setTrueSuccessor(null);
         setFalseSuccessor(null);
-        ((FixedWithNextNode) predecessor()).setNext(next);
+        pred.setNext(next);
         safeDelete();
         trueSuccessor.safeDelete();
         falseSuccessor.safeDelete();
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/LoopBeginNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/LoopBeginNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.nodes;
 
+import static com.oracle.graal.graph.iterators.NodePredicates.*;
+
 import java.util.*;
 
 import com.oracle.graal.graph.*;
@@ -49,8 +51,17 @@
         return usages().filter(LoopEndNode.class);
     }
 
+    public NodeIterable<LoopExitNode> loopExits() {
+        return usages().filter(LoopExitNode.class);
+    }
+
+    @Override
+    public NodeIterable<Node> anchored() {
+        return super.anchored().filter(isNotA(LoopEndNode.class).nor(LoopExitNode.class));
+    }
+
     public List<LoopEndNode> orderedLoopEnds() {
-        List<LoopEndNode> snapshot = usages().filter(LoopEndNode.class).snapshot();
+        List<LoopEndNode> snapshot = loopEnds().snapshot();
         Collections.sort(snapshot, new Comparator<LoopEndNode>() {
             @Override
             public int compare(LoopEndNode o1, LoopEndNode o2) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/LoopExitNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import com.oracle.graal.nodes.spi.*;
+
+public class LoopExitNode extends BeginNode {
+    @Input(notDataflow = true) private LoopBeginNode loopBegin;
+    public LoopExitNode(LoopBeginNode loop) {
+        assert loop != null;
+        loopBegin = loop;
+    }
+
+    public LoopBeginNode loopBegin() {
+        return loopBegin;
+    }
+
+    @Override
+    public void simplify(SimplifierTool tool) {
+        //
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/MergeNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -28,6 +28,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.util.*;
 
 /**
  * Denotes the merging of multiple control-flow paths.
@@ -115,12 +116,22 @@
     }
 
     public NodeIterable<PhiNode> phis() {
-        return this.usages().filter(new NodePredicate() {
+        return this.usages().filter(PhiNode.class).filter(new NodePredicate() {
             @Override
             public boolean apply(Node n) {
-                return n instanceof PhiNode && ((PhiNode) n).merge() == MergeNode.this;
+                return ((PhiNode) n).merge() == MergeNode.this;
             }
-        }).filter(PhiNode.class);
+        });
+    }
+
+    @Override
+    public NodeIterable<Node> anchored() {
+        return super.anchored().filter(isNotA(PhiNode.class).or(new NodePredicate() {
+            @Override
+            public boolean apply(Node n) {
+                return ((PhiNode) n).merge() != MergeNode.this;
+            }
+        }));
     }
 
     @Override
@@ -136,7 +147,9 @@
                     }
                 }
             }
-            Debug.log("Split %s into loop ends for %s", this, begin);
+            FixedNode evacuateAnchoredTo = new ComputeImmediateDominator(this).compute();
+            Debug.log("Split %s into loop ends for %s. Evacuate to %s", this, begin, evacuateAnchoredTo);
+            this.prepareDelete(evacuateAnchoredTo);
             int numEnds = this.forwardEndCount();
             StructuredGraph graph = (StructuredGraph) graph();
             for (int i = 0; i < numEnds - 1; i++) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PhiNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PhiNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -33,22 +33,15 @@
  * and a variable.
  */
 public final class PhiNode extends FloatingNode implements Canonicalizable, Node.IterableNodeType {
-
-    @Input(notDataflow = true) private MergeNode merge;
-
-    @Input private final NodeInputList<ValueNode> values = new NodeInputList<>(this);
-
-    public MergeNode merge() {
-        return merge;
-    }
-
     public static enum PhiType {
         Value, // normal value phis
         Memory, // memory phis
         Virtual // phis used for VirtualObjectField merges
     }
 
-    private final PhiType type;
+    @Input(notDataflow = true) private MergeNode merge;
+    @Input private final NodeInputList<ValueNode> values = new NodeInputList<>(this);
+    @Data private final PhiType type;
 
     public PhiNode(CiKind kind, MergeNode merge, PhiType type) {
         super(StampFactory.forKind(kind));
@@ -60,6 +53,10 @@
         return type;
     }
 
+    public MergeNode merge() {
+        return merge;
+    }
+
     public NodeInputList<ValueNode> values() {
         return values;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue Apr 10 12:22:46 2012 +0200
@@ -168,6 +168,9 @@
 
     public void removeFixed(FixedWithNextNode node) {
         assert node != null;
+        if (node instanceof BeginNode) {
+            ((BeginNode) node).prepareDelete();
+        }
         assert node.usages().isEmpty() : node + " " + node.usages();
         FixedNode next = node.next();
         node.setNext(null);
@@ -208,15 +211,11 @@
         assert node.usages().isEmpty();
         assert survivingSuccessor >= 0 && survivingSuccessor < node.blockSuccessorCount() : "invalid surviving successor " + survivingSuccessor + " for " + node;
         BeginNode begin = node.blockSuccessor(survivingSuccessor);
-        begin.evacuateGuards();
-        FixedNode next = begin.next();
-        begin.setNext(null);
         for (int i = 0; i < node.blockSuccessorCount(); i++) {
             node.setBlockSuccessor(i, null);
         }
-        node.replaceAtPredecessors(next);
+        node.replaceAtPredecessors(begin);
         node.safeDelete();
-        begin.safeDelete();
     }
 
     public void removeSplitPropagate(ControlSplitNode node, int survivingSuccessor) {
@@ -224,9 +223,6 @@
         assert node.usages().isEmpty();
         assert survivingSuccessor >= 0 && survivingSuccessor < node.blockSuccessorCount() : "invalid surviving successor " + survivingSuccessor + " for " + node;
         BeginNode begin = node.blockSuccessor(survivingSuccessor);
-        begin.evacuateGuards();
-        FixedNode next = begin.next();
-        begin.setNext(null);
         for (int i = 0; i < node.blockSuccessorCount(); i++) {
             BeginNode successor = node.blockSuccessor(i);
             node.setBlockSuccessor(i, null);
@@ -234,10 +230,9 @@
                 GraphUtil.killCFG(successor);
             }
         }
-        if (next.isAlive()) {
-            node.replaceAtPredecessors(next);
+        if (begin.isAlive()) {
+            node.replaceAtPredecessors(begin);
             node.safeDelete();
-            begin.safeDelete();
         } else {
             assert node.isDeleted();
         }
@@ -257,31 +252,23 @@
         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
         assert survivingSuccessor >= 0 && survivingSuccessor < node.blockSuccessorCount() : "invalid surviving successor " + survivingSuccessor + " for " + node;
         BeginNode begin = node.blockSuccessor(survivingSuccessor);
-        begin.evacuateGuards();
-        FixedNode next = begin.next();
-        begin.setNext(null);
         for (int i = 0; i < node.blockSuccessorCount(); i++) {
             node.setBlockSuccessor(i, null);
         }
-        replacement.setNext(next);
+        replacement.setNext(begin);
         node.replaceAndDelete(replacement);
-        begin.safeDelete();
     }
 
     public void replaceSplitWithFloating(ControlSplitNode node, FloatingNode replacement, int survivingSuccessor) {
         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
         assert survivingSuccessor >= 0 && survivingSuccessor < node.blockSuccessorCount() : "invalid surviving successor " + survivingSuccessor + " for " + node;
         BeginNode begin = node.blockSuccessor(survivingSuccessor);
-        begin.evacuateGuards();
-        FixedNode next = begin.next();
-        begin.setNext(null);
         for (int i = 0; i < node.blockSuccessorCount(); i++) {
             node.setBlockSuccessor(i, null);
         }
-        node.replaceAtPredecessors(next);
+        node.replaceAtPredecessors(begin);
         node.replaceAtUsages(replacement);
         node.safeDelete();
-        begin.safeDelete();
     }
 
     public void addAfterFixed(FixedWithNextNode node, FixedWithNextNode newNode) {
@@ -325,14 +312,15 @@
         EndNode singleEnd = merge.forwardEndAt(0);
         FixedNode sux = merge.next();
         FrameState stateAfter = merge.stateAfter();
+        // remove loop exits
+        if (merge instanceof LoopBeginNode) {
+            for (LoopExitNode exit : ((LoopBeginNode) merge).loopExits().snapshot()) {
+                exit.removeProxies();
+                replaceFixedWithFixed(exit, this.add(new BeginNode()));
+            }
+        }
         // evacuateGuards
-        Node prevBegin = singleEnd.predecessor();
-        assert prevBegin != null;
-        while (!(prevBegin instanceof BeginNode)) {
-            prevBegin = prevBegin.predecessor();
-        }
-        merge.replaceAtUsages(prevBegin);
-
+        merge.prepareDelete((FixedNode) singleEnd.predecessor());
         merge.safeDelete();
         if (stateAfter != null && stateAfter.usages().isEmpty()) {
             stateAfter.safeDelete();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012, 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;
+
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.Node.*;
+import com.oracle.graal.nodes.PhiNode.PhiType;
+import com.oracle.graal.nodes.calc.*;
+
+
+
+public class ValueProxyNode extends FloatingNode implements Node.IterableNodeType, ValueNumberable {
+    @Input(notDataflow = true) private BeginNode proxyPoint;
+    @Input private ValueNode value;
+    @Data private final PhiType type;
+
+    public ValueProxyNode(ValueNode value, BeginNode exit, PhiType type) {
+        super(value.stamp());
+        this.type = type;
+        assert exit != null;
+        this.proxyPoint = exit;
+        this.value = value;
+    }
+
+    public ValueNode value() {
+        return value;
+    }
+
+    public BeginNode proxyPoint() {
+        return proxyPoint;
+    }
+
+    public PhiType type() {
+        return type;
+    }
+
+    @Override
+    public boolean verify() {
+        assert value != null;
+        assert proxyPoint != null;
+        return super.verify();
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerMulNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerMulNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -52,7 +52,7 @@
                 return x();
             }
             if (c == 0) {
-                return ConstantNode.forInt(0, graph());
+                return ConstantNode.defaultForKind(kind(), graph());
             }
             if (c > 0 && CiUtil.isPowerOf2(c)) {
                 return graph().unique(new LeftShiftNode(kind(), x(), ConstantNode.forInt(CiUtil.log2(c), graph())));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessIndexedNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessIndexedNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -36,6 +36,8 @@
 
     @Input private ValueNode index;
     @Input private ValueNode length;
+    @Data private final CiKind elementType;
+    private final long leafGraphId;
 
     public ValueNode index() {
         return index;
@@ -45,9 +47,6 @@
         return length;
     }
 
-    private final CiKind elementType;
-    private final long leafGraphId;
-
     /**
      * Create an new AccessIndexedNode.
      * @param kind the result kind of the access
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -39,7 +39,7 @@
  */
 public final class CheckCastNode extends TypeCheckNode implements Canonicalizable, LIRLowerable, Node.IterableNodeType, TypeFeedbackProvider, TypeCanonicalizable {
 
-    @Input protected final FixedNode anchor;
+    @Input(notDataflow = true) protected final FixedNode anchor;
     @Data  protected final boolean emitCode;
 
     public FixedNode anchor() {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.spi.types.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
 
 /**
  * The {@code NewArrayNode} class is the base of all instructions that allocate arrays.
@@ -130,7 +131,7 @@
         public int updateState(Node node, Node current, Map<Object, Integer> fieldIndex, ValueNode[] fieldState) {
             if (current instanceof AccessIndexedNode) {
                 AccessIndexedNode x = (AccessIndexedNode) current;
-                if (x.array() == node) {
+                if (GraphUtil.unProxify(x.array()) == node) {
                     int index = ((AccessIndexedNode) current).index().asConstant().asInt();
                     if (current instanceof LoadIndexedNode) {
                         x.replaceAtUsages(fieldState[index]);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -29,6 +29,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
 
 /**
  * The {@code NewInstanceNode} represents the allocation of an instance class object.
@@ -110,7 +111,7 @@
         public int updateState(Node node, Node current, Map<Object, Integer> fieldIndex, ValueNode[] fieldState) {
             if (current instanceof AccessFieldNode) {
                 AccessFieldNode x = (AccessFieldNode) current;
-                if (x.object() == node) {
+                if (GraphUtil.unProxify(x.object()) == node) {
                     int field = fieldIndex.get(x.field());
                     StructuredGraph graph = (StructuredGraph) x.graph();
                     if (current instanceof LoadFieldNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java	Tue Apr 10 12:22:46 2012 +0200
@@ -81,6 +81,13 @@
         } else if (usage instanceof ArrayLengthNode) {
             assert ((ArrayLengthNode) usage).array() == node;
             return false;
+        } else if (usage instanceof ValueProxyNode) {
+            for (Node vpnUsage : usage.usages().snapshot()) {
+                if (escape(usage, vpnUsage)) {
+                    return true;
+                }
+            }
+            return false;
         } else {
             return true;
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Tue Apr 10 12:22:46 2012 +0200
@@ -27,8 +27,10 @@
 import java.util.*;
 
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.virtual.*;
 
 public class GraphUtil {
 
@@ -40,7 +42,11 @@
             killEnd(end);
         } else {
             // Normal control flow node.
-            for (Node successor : node.successors().snapshot()) {
+            /* We do not take a successor snapshot because this iterator supports concurrent modifications
+             * as long as they do not change the size of the successor list. Not tasking a snapshot allows
+             * us to see modifications to other branches that may happen while processing one branch.
+             */
+            for (Node successor : node.successors()) {
                 killCFG((FixedNode) successor);
             }
         }
@@ -50,28 +56,39 @@
     private static void killEnd(EndNode end) {
         MergeNode merge = end.merge();
         merge.removeEnd(end);
+        StructuredGraph graph = (StructuredGraph) end.graph();
         if (merge instanceof LoopBeginNode && merge.forwardEndCount() == 0) { //dead loop
             for (PhiNode phi : merge.phis().snapshot()) {
                 propagateKill(phi);
             }
             LoopBeginNode begin = (LoopBeginNode) merge;
-            // disconnect and delete loop ends
+            // disconnect and delete loop ends & loop exits
             for (LoopEndNode loopend : begin.loopEnds().snapshot()) {
                 loopend.predecessor().replaceFirstSuccessor(loopend, null);
                 loopend.safeDelete();
             }
+            for (LoopExitNode loopexit : begin.loopExits().snapshot()) {
+                for (ValueProxyNode vpn : loopexit.proxies().snapshot()) {
+                    graph.replaceFloating(vpn, vpn.value());
+                }
+                graph.replaceFixedWithFixed(loopexit, graph.add(new BeginNode()));
+            }
             killCFG(begin.next());
             begin.safeDelete();
         } else if (merge instanceof LoopBeginNode && ((LoopBeginNode) merge).loopEnds().isEmpty()) { // not a loop anymore
-            ((StructuredGraph) end.graph()).reduceDegenerateLoopBegin((LoopBeginNode) merge);
+            graph.reduceDegenerateLoopBegin((LoopBeginNode) merge);
         } else if (merge.phiPredecessorCount() == 1) { // not a merge anymore
-            ((StructuredGraph) end.graph()).reduceTrivialMerge(merge);
+            graph.reduceTrivialMerge(merge);
         }
     }
 
+    public static NodePredicate isFloatingNode() {
+        return isA(FloatingNode.class).or(CallTargetNode.class).or(FrameState.class).or(VirtualObjectFieldNode.class).or(VirtualObjectNode.class);
+    }
+
     public static void propagateKill(Node node) {
         if (node != null && node.isAlive()) {
-            List<Node> usagesSnapshot = node.usages().filter(isA(FloatingNode.class).or(CallTargetNode.class).or(FrameState.class)).snapshot();
+            List<Node> usagesSnapshot = node.usages().filter(isFloatingNode()).snapshot();
 
             // null out remaining usages
             node.replaceAtUsages(null);
@@ -91,7 +108,7 @@
     }
 
     public static void killUnusedFloatingInputs(Node node) {
-        List<Node> floatingInputs = node.inputs().filter(isA(FloatingNode.class).or(CallTargetNode.class).or(FrameState.class)).snapshot();
+        List<Node> floatingInputs = node.inputs().filter(isFloatingNode()).snapshot();
         node.safeDelete();
 
         for (Node in : floatingInputs) {
@@ -100,4 +117,70 @@
             }
         }
     }
+
+    public static void checkRedundantPhi(PhiNode phiNode) {
+        if (phiNode.isDeleted() || phiNode.valueCount() == 1) {
+            return;
+        }
+
+        ValueNode singleValue = phiNode.singleValue();
+        if (singleValue != null) {
+            Collection<PhiNode> phiUsages = phiNode.usages().filter(PhiNode.class).snapshot();
+            Collection<ValueProxyNode> proxyUsages = phiNode.usages().filter(ValueProxyNode.class).snapshot();
+            ((StructuredGraph) phiNode.graph()).replaceFloating(phiNode, singleValue);
+            for (PhiNode phi : phiUsages) {
+                checkRedundantPhi(phi);
+            }
+            for (ValueProxyNode proxy : proxyUsages) {
+                checkRedundantProxy(proxy);
+            }
+        }
+    }
+
+    public static void checkRedundantProxy(ValueProxyNode vpn) {
+        BeginNode proxyPoint = vpn.proxyPoint();
+        if (proxyPoint instanceof LoopExitNode) {
+            LoopExitNode exit = (LoopExitNode) proxyPoint;
+            LoopBeginNode loopBegin = exit.loopBegin();
+            ValueNode vpnValue = vpn.value();
+            for (ValueNode v : loopBegin.stateAfter().values()) {
+                ValueNode v2 = v;
+                if (loopBegin.isPhiAtMerge(v2)) {
+                    v2 = ((PhiNode) v2).valueAt(loopBegin.forwardEnd());
+                }
+                if (vpnValue == v2) {
+                    Collection<PhiNode> phiUsages = vpn.usages().filter(PhiNode.class).snapshot();
+                    Collection<ValueProxyNode> proxyUsages = vpn.usages().filter(ValueProxyNode.class).snapshot();
+                    ((StructuredGraph) vpn.graph()).replaceFloating(vpn, vpnValue);
+                    for (PhiNode phi : phiUsages) {
+                        checkRedundantPhi(phi);
+                    }
+                    for (ValueProxyNode proxy : proxyUsages) {
+                        checkRedundantProxy(proxy);
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+    public static void normalizeLoopBegin(LoopBeginNode begin) {
+        // Delete unnecessary loop phi functions, i.e., phi functions where all inputs are either the same or the phi itself.
+        for (PhiNode phi : begin.phis().snapshot()) {
+            GraphUtil.checkRedundantPhi(phi);
+        }
+        for (LoopExitNode exit : begin.loopExits()) {
+            for (ValueProxyNode vpn : exit.proxies().snapshot()) {
+                GraphUtil.checkRedundantProxy(vpn);
+            }
+        }
+    }
+
+    public static ValueNode unProxify(ValueNode proxy) {
+        ValueNode v = proxy;
+        while (v instanceof ValueProxyNode) {
+            v = ((ValueProxyNode) v).value();
+        }
+        return v;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectFieldNode.java	Sun Apr 08 00:09:10 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectFieldNode.java	Tue Apr 10 12:22:46 2012 +0200
@@ -66,6 +66,13 @@
     }
 
     @Override
+    public boolean verify() {
+        assertTrue(object != null, "No object");
+        assertTrue(input != null, "No input");
+        return super.verify();
+    }
+
+    @Override
     public Map<Object, Object> getDebugProperties() {
         Map<Object, Object> properties = super.getDebugProperties();
         properties.put("index", index);
@@ -74,7 +81,7 @@
 
     @Override
     public String toString(Verbosity verbosity) {
-        if (verbosity == Verbosity.Name && object().fields() != null) {
+        if (verbosity == Verbosity.Name && object() != null && object().fields() != null) {
             return super.toString(Verbosity.Name) + " " + object().fields()[index].name();
         } else {
             return super.toString(verbosity);