changeset 19235:9f411d79ffc0

Merge (LowLevelSuites).
author Josef Eisl <josef.eisl@jku.at>
date Tue, 10 Feb 2015 14:43:12 +0100
parents e9e99e6f2c6b (diff) 948e62909539 (current diff)
children ba6ce79ad299
files graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java
diffstat 48 files changed, 1304 insertions(+), 738 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CompilationResult.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CompilationResult.java	Tue Feb 10 14:43:12 2015 +0100
@@ -684,10 +684,38 @@
      * @param handlerPos the position of the handler
      */
     public void recordExceptionHandler(int codePos, int handlerPos) {
+        assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos);
         exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos));
     }
 
     /**
+     * Validate if the exception handler for codePos already exists and handlerPos is different.
+     *
+     * @param codePos
+     * @param handlerPos
+     * @return true if the validation is successful
+     */
+    private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) {
+        ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos);
+        return exHandler == null || exHandler.handlerPos == handlerPos;
+    }
+
+    /**
+     * Returns the first ExceptionHandler which matches codePos.
+     *
+     * @param codePos position to search for
+     * @return first matching ExceptionHandler
+     */
+    private ExceptionHandler getExceptionHandlerForCodePos(int codePos) {
+        for (ExceptionHandler h : exceptionHandlers) {
+            if (h.pcOffset == codePos) {
+                return h;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Records an infopoint in the code array.
      *
      * @param codePos the position of the infopoint in the code array
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/DataSection.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/DataSection.java	Tue Feb 10 14:43:12 2015 +0100
@@ -47,27 +47,8 @@
             return (buffer, patch) -> buffer.put(data);
         }
 
-        static DataBuilder primitive(PrimitiveConstant c) {
-            switch (c.getKind()) {
-                case Boolean:
-                    return (buffer, patch) -> buffer.put(c.asBoolean() ? (byte) 1 : (byte) 0);
-                case Byte:
-                    return (buffer, patch) -> buffer.put((byte) c.asInt());
-                case Char:
-                    return (buffer, patch) -> buffer.putChar((char) c.asInt());
-                case Short:
-                    return (buffer, patch) -> buffer.putShort((short) c.asInt());
-                case Int:
-                    return (buffer, patch) -> buffer.putInt(c.asInt());
-                case Long:
-                    return (buffer, patch) -> buffer.putLong(c.asLong());
-                case Float:
-                    return (buffer, patch) -> buffer.putFloat(c.asFloat());
-                case Double:
-                    return (buffer, patch) -> buffer.putDouble(c.asDouble());
-                default:
-                    throw new IllegalArgumentException();
-            }
+        static DataBuilder serializable(SerializableConstant c) {
+            return (buffer, patch) -> c.serialize(buffer);
         }
 
         static DataBuilder zero(int size) {
--- a/graal/com.oracle.graal.asm.amd64.test/src/com/oracle/graal/asm/amd64/test/SimpleAssemblerTest.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.asm.amd64.test/src/com/oracle/graal/asm/amd64/test/SimpleAssemblerTest.java	Tue Feb 10 14:43:12 2015 +0100
@@ -68,7 +68,7 @@
             public byte[] generateCode(CompilationResult compResult, TargetDescription target, RegisterConfig registerConfig, CallingConvention cc) {
                 AMD64MacroAssembler asm = new AMD64MacroAssembler(target, registerConfig);
                 Register ret = registerConfig.getReturnRegister(Kind.Double);
-                Data data = new Data(8, 8, DataBuilder.primitive(JavaConstant.forDouble(84.72)));
+                Data data = new Data(8, 8, DataBuilder.serializable(JavaConstant.forDouble(84.72)));
                 DataSectionReference ref = compResult.getDataSection().insertData(data);
                 compResult.recordDataPatch(asm.position(), ref);
                 asm.movdbl(ret, asm.getPlaceholder());
--- a/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java	Tue Feb 10 14:43:12 2015 +0100
@@ -107,7 +107,7 @@
             frameState.clearNonLiveLocals(blockMap.startBlock, liveness, true);
 
             currentBlock = blockMap.startBlock;
-            blockMap.startBlock.entryState = frameState;
+            blockMap.startBlock.setEntryState(0, frameState);
             if (blockMap.startBlock.isLoopHeader) {
                 throw GraalInternalError.unimplemented("Handle start block as loop header");
             }
@@ -609,15 +609,15 @@
                  */
                 moveConstantsToVariables();
             }
-            block.entryState = frameState.copy();
-            block.entryState.clearNonLiveLocals(block, liveness, true);
+            block.setEntryState(0, frameState.copy());
+            block.getEntryState(0).clearNonLiveLocals(block, liveness, true);
 
             Debug.log("createTarget %s: first visit", block);
             return;
         }
 
         // We already saw this block before, so we have to merge states.
-        if (!((BaselineFrameStateBuilder) block.entryState).isCompatibleWith(frameState)) {
+        if (!((BaselineFrameStateBuilder) block.getEntryState(0)).isCompatibleWith(frameState)) {
             throw new BailoutException("stacks do not match; bytecodes would not verify");
         }
 
@@ -625,7 +625,7 @@
             assert currentBlock == null || currentBlock.getId() >= block.getId() : "must be backward branch";
             if (currentBlock != null && currentBlock.numNormalSuccessors() == 1) {
                 // this is the only successor of the current block so we can adjust
-                adaptFramestate((BaselineFrameStateBuilder) block.entryState);
+                adaptFramestate((BaselineFrameStateBuilder) block.getEntryState(0));
                 return;
             }
             GraalInternalError.unimplemented("Loops not yet supported");
@@ -639,7 +639,7 @@
          */
         if (currentBlock != null && currentBlock.numNormalSuccessors() == 1) {
             // this is the only successor of the current block so we can adjust
-            adaptFramestate((BaselineFrameStateBuilder) block.entryState);
+            adaptFramestate((BaselineFrameStateBuilder) block.getEntryState(0));
             return;
         }
         GraalInternalError.unimplemented("second block visit not yet implemented");
@@ -700,7 +700,7 @@
     }
 
     protected void processBlock(BciBlock block) {
-        frameState = (BaselineFrameStateBuilder) block.entryState;
+        frameState = (BaselineFrameStateBuilder) block.getEntryState(0);
         setCurrentFrameState(frameState);
         currentBlock = block;
         iterateBytecodesForBlock(block);
@@ -730,7 +730,7 @@
              * We need to preserve the frame state builder of the loop header so that we can merge
              * values for phi functions, so make a copy of it.
              */
-            block.entryState = frameState.copy();
+            block.setEntryState(0, frameState.copy());
 
         }
         int endBCI = stream.endBCI();
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Tue Feb 10 14:43:12 2015 +0100
@@ -343,6 +343,9 @@
     public static final OptionValue<Boolean> ImplicitStableValues = new OptionValue<>(true);
 
 
+    @Option(help = "Max number of loop explosions per method.", type = OptionType.Debug)
+    public static final OptionValue<Integer> MaximumLoopExplosionCount = new OptionValue<>(10000);
+
     /**
      * Counts the various paths taken through snippets.
      */
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue Feb 10 14:43:12 2015 +0100
@@ -123,7 +123,7 @@
         long finalDisp = displacement;
         if (isConstant(base)) {
             if (asConstant(base).isNull()) {
-                baseRegister = SPARC.g0.asValue();
+                baseRegister = SPARC.g0.asValue(base.getLIRKind());
             } else if (asConstant(base).getKind() != Kind.Object) {
                 finalDisp += asConstant(base).asLong();
                 baseRegister = Value.ILLEGAL;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue Feb 10 14:43:12 2015 +0100
@@ -56,6 +56,7 @@
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.schedule.*;
@@ -88,7 +89,7 @@
 
     private final Providers providers;
     private final Backend backend;
-    private final Suites suites;
+    private final DerivedOptionValue<Suites> suites;
     private final LowLevelSuites lowLevelSuites;
 
     /**
@@ -173,7 +174,7 @@
     public GraalCompilerTest() {
         this.backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend();
         this.providers = getBackend().getProviders();
-        this.suites = createSuites();
+        this.suites = new DerivedOptionValue<>(this::createSuites);
         this.lowLevelSuites = createLowLevelSuites();
         installSubstitutions();
     }
@@ -194,7 +195,7 @@
             this.backend = runtime.getHostBackend();
         }
         this.providers = backend.getProviders();
-        this.suites = createSuites();
+        this.suites = new DerivedOptionValue<>(this::createSuites);
         this.lowLevelSuites = createLowLevelSuites();
         installSubstitutions();
     }
@@ -360,7 +361,7 @@
     }
 
     protected Suites getSuites() {
-        return suites;
+        return suites.getValue();
     }
 
     protected LowLevelSuites getLowLevelSuites() {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Feb 10 14:43:12 2015 +0100
@@ -281,7 +281,7 @@
         try (TimerCloseable a = BackEnd.start()) {
             LIRGenerationResult lirGen = null;
             lirGen = emitLIR(backend, target, schedule, graph, stub, cc, registerConfig, lowLevelSuites);
-            try (Scope s = Debug.scope("CodeGen", lirGen)) {
+            try (Scope s = Debug.scope("CodeGen", lirGen, lirGen.getLIR())) {
                 emitCode(backend, assumptions, lirGen, compilationResult, installedCodeOwner, factory);
             } catch (Throwable e) {
                 throw Debug.handle(e);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotCodeCacheProvider.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotCodeCacheProvider.java	Tue Feb 10 14:43:12 2015 +0100
@@ -327,10 +327,10 @@
             boolean compressed = COMPRESSED_NULL.equals(constant);
             size = target.getSizeInBytes(compressed ? Kind.Int : target.wordKind);
             builder = DataBuilder.zero(size);
-        } else if (constant instanceof PrimitiveConstant) {
-            PrimitiveConstant prim = (PrimitiveConstant) constant;
-            size = target.getSizeInBytes(prim.getKind());
-            builder = DataBuilder.primitive(prim);
+        } else if (constant instanceof SerializableConstant) {
+            SerializableConstant s = (SerializableConstant) constant;
+            size = s.getSerializedSize();
+            builder = DataBuilder.serializable(s);
         } else {
             throw GraalInternalError.shouldNotReachHere();
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPluginsProvider.java	Tue Feb 10 14:43:12 2015 +0100
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 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.hotspot.meta;
+
+import static com.oracle.graal.java.GraphBuilderContext.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.hotspot.replacements.*;
+import com.oracle.graal.java.*;
+import com.oracle.graal.java.GraphBuilderPlugins.InvocationPlugin;
+import com.oracle.graal.java.GraphBuilderPlugins.Registration;
+import com.oracle.graal.java.GraphBuilderPlugins.Registration.Receiver;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.options.*;
+
+/**
+ * Provider of HotSpot specific {@link GraphBuilderPlugin}s.
+ */
+@ServiceProvider(GraphBuilderPluginsProvider.class)
+public class HotSpotGraphBuilderPluginsProvider implements GraphBuilderPluginsProvider {
+    public void registerPlugins(MetaAccessProvider metaAccess, GraphBuilderPlugins plugins) {
+        // Object.class
+        Registration r = new Registration(plugins, metaAccess, Object.class);
+        r.register1("getClass", Receiver.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode rcvr) {
+                ObjectStamp objectStamp = (ObjectStamp) rcvr.stamp();
+                ValueNode mirror;
+                if (objectStamp.isExactType() && objectStamp.nonNull()) {
+                    mirror = builder.append(ConstantNode.forConstant(objectStamp.type().getJavaClass(), builder.getMetaAccess()));
+                } else {
+                    StampProvider stampProvider = builder.getStampProvider();
+                    LoadHubNode hub = builder.append(new LoadHubNode(stampProvider, nullCheckedValue(builder, rcvr)));
+                    mirror = builder.append(new HubGetClassNode(builder.getMetaAccess(), hub));
+                }
+                builder.push(Kind.Object, mirror);
+                return true;
+            }
+        });
+
+        // Class.class
+        r = new Registration(plugins, metaAccess, Class.class);
+        r.register2("cast", Receiver.class, Object.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode rcvr, ValueNode object) {
+                if (rcvr.isConstant() && !rcvr.isNullConstant() && object.isConstant()) {
+                    ResolvedJavaType type = builder.getConstantReflection().asJavaType(rcvr.asConstant());
+                    if (type != null && !type.isPrimitive() && type.isInstance(object.asJavaConstant())) {
+                        builder.push(Kind.Object, object);
+                        return true;
+                    }
+                }
+                return false;
+            }
+        });
+        // StableOptionValue.class
+        r = new Registration(plugins, metaAccess, StableOptionValue.class);
+        r.register1("getValue", Receiver.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode rcvr) {
+                if (rcvr.isConstant() && !rcvr.isNullConstant()) {
+                    Object object = ((HotSpotObjectConstantImpl) rcvr.asConstant()).object();
+                    StableOptionValue<?> option = (StableOptionValue<?>) object;
+                    ConstantNode value = builder.append(ConstantNode.forConstant(HotSpotObjectConstantImpl.forObject(option.getValue()), builder.getMetaAccess()));
+                    builder.push(Kind.Object, value);
+                    return true;
+                }
+                return false;
+            }
+        });
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Tue Feb 10 14:43:12 2015 +0100
@@ -24,8 +24,6 @@
 
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 
-import java.util.function.*;
-
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.hotspot.*;
@@ -36,13 +34,16 @@
 import com.oracle.graal.java.GraphBuilderPlugins.InlineInvokePlugin;
 import com.oracle.graal.lir.phases.*;
 import com.oracle.graal.options.*;
+import com.oracle.graal.options.DerivedOptionValue.OptionSupplier;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.tiers.*;
 
 /**
  * HotSpot implementation of {@link SuitesProvider}.
  */
-public class HotSpotSuitesProvider implements SuitesProvider, Supplier<Suites> {
+public class HotSpotSuitesProvider implements SuitesProvider, OptionSupplier<Suites> {
+
+    private static final long serialVersionUID = -5755004498526945687L;
 
     protected final DerivedOptionValue<Suites> defaultSuites;
     protected final PhaseSuite<HighTierContext> defaultGraphBuilderSuite;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotGraphBuilderPluginsProvider.java	Tue Feb 10 11:15:39 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2015, 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.hotspot.replacements;
-
-import static com.oracle.graal.java.GraphBuilderContext.*;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.runtime.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.java.*;
-import com.oracle.graal.java.GraphBuilderPlugins.InvocationPlugin;
-import com.oracle.graal.java.GraphBuilderPlugins.Registration;
-import com.oracle.graal.java.GraphBuilderPlugins.Registration.Receiver;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.spi.*;
-
-/**
- * Provider of HotSpot specific {@link GraphBuilderPlugin}s.
- */
-@ServiceProvider(GraphBuilderPluginsProvider.class)
-public class HotSpotGraphBuilderPluginsProvider implements GraphBuilderPluginsProvider {
-    public void registerPlugins(MetaAccessProvider metaAccess, GraphBuilderPlugins plugins) {
-        Registration r = new Registration(plugins, metaAccess, Object.class);
-        r.register1("getClass", Receiver.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode rcvr) {
-                ObjectStamp objectStamp = (ObjectStamp) rcvr.stamp();
-                ValueNode mirror;
-                if (objectStamp.isExactType() && objectStamp.nonNull()) {
-                    mirror = builder.append(ConstantNode.forConstant(objectStamp.type().getJavaClass(), metaAccess));
-                } else {
-                    StampProvider stampProvider = builder.getStampProvider();
-                    LoadHubNode hub = builder.append(new LoadHubNode(stampProvider, nullCheckedValue(builder, rcvr)));
-                    mirror = builder.append(new HubGetClassNode(builder.getMetaAccess(), hub));
-                }
-                builder.push(Kind.Object, mirror);
-                return true;
-            }
-        });
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HubGetClassNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HubGetClassNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -41,7 +41,7 @@
 public class HubGetClassNode extends FloatingGuardedNode implements Lowerable, Canonicalizable, ConvertNode {
     @Input protected ValueNode hub;
 
-    protected HubGetClassNode(@InjectedNodeParameter MetaAccessProvider metaAccess, ValueNode hub) {
+    public HubGetClassNode(@InjectedNodeParameter MetaAccessProvider metaAccess, ValueNode hub) {
         super(StampFactory.declaredNonNull(metaAccess.lookupJavaType(Class.class)), null);
         this.hub = hub;
     }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Tue Feb 10 14:43:12 2015 +0100
@@ -81,8 +81,8 @@
         public int endBci;
         public boolean isExceptionEntry;
         public boolean isLoopHeader;
-        public boolean isLastLoopEnd;
         public int loopId;
+        public int loopEnd;
 
         /**
          * XXX to be removed - currently only used by baseline compiler.
@@ -90,8 +90,10 @@
         public Loop<BciBlock> loop;
         public boolean isLoopEnd;
 
-        public FixedWithNextNode firstInstruction;
-        public AbstractFrameStateBuilder<?, ?> entryState;
+        private FixedWithNextNode firstInstruction;
+        private AbstractFrameStateBuilder<?, ?> entryState;
+        private FixedWithNextNode[] firstInstructionArray;
+        private AbstractFrameStateBuilder<?, ?>[] entryStateArray;
 
         private boolean visited;
         private boolean active;
@@ -330,6 +332,66 @@
         public void setJsrReturnBci(int bci) {
             this.getOrCreateJSRData().jsrReturnBci = bci;
         }
+
+        public FixedWithNextNode getFirstInstruction(int dimension) {
+            if (dimension == 0) {
+                return firstInstruction;
+            } else {
+                if (firstInstructionArray != null && dimension - 1 < firstInstructionArray.length) {
+                    return firstInstructionArray[dimension - 1];
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public void setFirstInstruction(int dimension, FixedWithNextNode firstInstruction) {
+            if (dimension == 0) {
+                this.firstInstruction = firstInstruction;
+            } else {
+                if (firstInstructionArray == null) {
+                    firstInstructionArray = new FixedWithNextNode[4];
+                }
+                if (dimension - 1 < firstInstructionArray.length) {
+                    // We are within bounds.
+                } else {
+                    // We are out of bounds.
+                    firstInstructionArray = Arrays.copyOf(firstInstructionArray, Math.max(firstInstructionArray.length * 2, dimension));
+                }
+
+                firstInstructionArray[dimension - 1] = firstInstruction;
+            }
+        }
+
+        public AbstractFrameStateBuilder<?, ?> getEntryState(int dimension) {
+            if (dimension == 0) {
+                return entryState;
+            } else {
+                if (entryStateArray != null && dimension - 1 < entryStateArray.length) {
+                    return entryStateArray[dimension - 1];
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public void setEntryState(int dimension, AbstractFrameStateBuilder<?, ?> entryState) {
+            if (dimension == 0) {
+                this.entryState = entryState;
+            } else {
+                if (entryStateArray == null) {
+                    entryStateArray = new AbstractFrameStateBuilder<?, ?>[4];
+                }
+                if (dimension - 1 < entryStateArray.length) {
+                    // We are within bounds.
+                } else {
+                    // We are out of bounds.
+                    entryStateArray = Arrays.copyOf(entryStateArray, Math.max(entryStateArray.length * 2, dimension));
+                }
+
+                entryStateArray[dimension - 1] = entryState;
+            }
+        }
     }
 
     public static class ExceptionDispatchBlock extends BciBlock {
@@ -775,36 +837,24 @@
             }
         }
         blocks = newBlocks;
-
-        if (consecutiveLoopBlocks && this.nextLoop > 2) {
-            System.out.println();
-            for (int i = 0; i < blocks.length; ++i) {
-                String succ = "";
-                for (BciBlock succBlock : blocks[i].getSuccessors()) {
-                    succ += succBlock.getId() + " ";
-                }
-                System.out.printf("%3s %10s %s %s succ=[%s]\n", blocks[i].getId(), Long.toBinaryString(blocks[i].loops), blocks[i].isLoopHeader, blocks[i].isLastLoopEnd, succ);
-            }
-            System.out.println();
-        }
     }
 
     private int handleLoopHeader(BciBlock[] newBlocks, int nextStart, int i, BciBlock loopHeader) {
         int next = nextStart;
-        BciBlock last = loopHeader;
+        int endOfLoop = nextStart - 1;
         for (int j = i + 1; j < blocks.length; ++j) {
             BciBlock other = blocks[j];
             if (other != null && (other.loops & (1L << loopHeader.loopId)) != 0) {
                 other.setId(next);
+                endOfLoop = next;
                 newBlocks[next++] = other;
-                last = other;
                 blocks[j] = null;
                 if (other.isLoopHeader) {
                     next = handleLoopHeader(newBlocks, next, j, other);
                 }
             }
         }
-        last.isLastLoopEnd = true;
+        loopHeader.loopEnd = endOfLoop;
         return next;
     }
 
@@ -1030,6 +1080,11 @@
         public abstract boolean localIsLiveIn(BciBlock block, int local);
 
         /**
+         * Returns whether the local is set in the given loop.
+         */
+        public abstract boolean localIsChangedInLoop(int loopId, int local);
+
+        /**
          * Returns whether the local is live at the end of the given block.
          */
         public abstract boolean localIsLiveOut(BciBlock block, int local);
@@ -1084,6 +1139,7 @@
                 return;
             }
             int blockID = block.getId();
+            int localIndex;
             stream.setBCI(block.startBci);
             while (stream.currentBCI() <= block.endBci) {
                 switch (stream.currentBC()) {
@@ -1107,8 +1163,12 @@
                     case DLOAD_3:
                         loadTwo(blockID, 3);
                         break;
+                    case IINC:
+                        localIndex = stream.readLocalIndex();
+                        loadOne(blockID, localIndex);
+                        storeOne(blockID, localIndex);
+                        break;
                     case ILOAD:
-                    case IINC:
                     case FLOAD:
                     case ALOAD:
                     case RET:
@@ -1215,6 +1275,7 @@
         private final long[] localsLiveOut;
         private final long[] localsLiveGen;
         private final long[] localsLiveKill;
+        private final long[] localsChangedInLoop;
 
         public SmallLocalLiveness() {
             int blockSize = blocks.length;
@@ -1222,6 +1283,7 @@
             localsLiveOut = new long[blockSize];
             localsLiveGen = new long[blockSize];
             localsLiveKill = new long[blockSize];
+            localsChangedInLoop = new long[BciBlockMapping.this.nextLoop];
         }
 
         private String debugString(long value) {
@@ -1288,6 +1350,17 @@
             if ((localsLiveGen[blockID] & bit) == 0L) {
                 localsLiveKill[blockID] |= bit;
             }
+
+            BciBlock block = blocks[blockID];
+            long tmp = block.loops;
+            int pos = 0;
+            while (tmp != 0) {
+                if ((tmp & 1L) == 1L) {
+                    this.localsChangedInLoop[pos] |= bit;
+                }
+                tmp >>= 1;
+                ++pos;
+            }
         }
 
         @Override
@@ -1301,6 +1374,11 @@
             int blockID = block.getId();
             return blockID >= Integer.MAX_VALUE ? false : (localsLiveOut[blockID] & (1L << local)) != 0L;
         }
+
+        @Override
+        public boolean localIsChangedInLoop(int loopId, int local) {
+            return (localsChangedInLoop[loopId] & (1L << local)) != 0L;
+        }
     }
 
     public final class LargeLocalLiveness extends LocalLiveness {
@@ -1308,6 +1386,7 @@
         private BitSet[] localsLiveOut;
         private BitSet[] localsLiveGen;
         private BitSet[] localsLiveKill;
+        private BitSet[] localsChangedInLoop;
 
         public LargeLocalLiveness() {
             int blocksSize = blocks.length;
@@ -1315,11 +1394,16 @@
             localsLiveOut = new BitSet[blocksSize];
             localsLiveGen = new BitSet[blocksSize];
             localsLiveKill = new BitSet[blocksSize];
+            int maxLocals = method.getMaxLocals();
             for (int i = 0; i < blocksSize; i++) {
-                localsLiveIn[i] = new BitSet(method.getMaxLocals());
-                localsLiveOut[i] = new BitSet(method.getMaxLocals());
-                localsLiveGen[i] = new BitSet(method.getMaxLocals());
-                localsLiveKill[i] = new BitSet(method.getMaxLocals());
+                localsLiveIn[i] = new BitSet(maxLocals);
+                localsLiveOut[i] = new BitSet(maxLocals);
+                localsLiveGen[i] = new BitSet(maxLocals);
+                localsLiveKill[i] = new BitSet(maxLocals);
+            }
+            localsChangedInLoop = new BitSet[nextLoop];
+            for (int i = 0; i < nextLoop; ++i) {
+                localsChangedInLoop[i] = new BitSet(maxLocals);
             }
         }
 
@@ -1374,6 +1458,17 @@
             if (!localsLiveGen[blockID].get(local)) {
                 localsLiveKill[blockID].set(local);
             }
+
+            BciBlock block = blocks[blockID];
+            long tmp = block.loops;
+            int pos = 0;
+            while (tmp != 0) {
+                if ((tmp & 1L) == 1L) {
+                    this.localsChangedInLoop[pos].set(local);
+                }
+                tmp >>= 1;
+                ++pos;
+            }
         }
 
         @Override
@@ -1385,6 +1480,11 @@
         public boolean localIsLiveOut(BciBlock block, int local) {
             return block.getId() >= Integer.MAX_VALUE ? true : localsLiveOut[block.getId()].get(local);
         }
+
+        @Override
+        public boolean localIsChangedInLoop(int loopId, int local) {
+            return localsChangedInLoop[loopId].get(local);
+        }
     }
 
     public BciBlock[] getLoopHeaders() {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderContext.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderContext.java	Tue Feb 10 14:43:12 2015 +0100
@@ -47,6 +47,8 @@
 
     Assumptions getAssumptions();
 
+    ConstantReflectionProvider getConstantReflection();
+
     void push(Kind kind, ValueNode value);
 
     /**
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Feb 10 14:43:12 2015 +0100
@@ -37,8 +37,8 @@
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.Node.ValueNumberable;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
@@ -102,7 +102,7 @@
         private final OptimisticOptimizations optimisticOpts;
         private final StampProvider stampProvider;
         private final Assumptions assumptions;
-        private final ConstantReflectionProvider constantReflectionProvider;
+        private final ConstantReflectionProvider constantReflection;
 
         /**
          * Gets the graph being processed by this builder.
@@ -111,7 +111,7 @@
             return currentGraph;
         }
 
-        public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, Assumptions assumptions, ConstantReflectionProvider constantReflectionProvider,
+        public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, Assumptions assumptions, ConstantReflectionProvider constantReflection,
                         GraphBuilderConfiguration graphBuilderConfig, GraphBuilderPlugins graphBuilderPlugins, OptimisticOptimizations optimisticOpts) {
             this.graphBuilderConfig = graphBuilderConfig;
             this.optimisticOpts = optimisticOpts;
@@ -119,13 +119,13 @@
             this.stampProvider = stampProvider;
             this.assumptions = assumptions;
             this.graphBuilderPlugins = graphBuilderPlugins;
-            this.constantReflectionProvider = constantReflectionProvider;
+            this.constantReflection = constantReflection;
             assert metaAccess != null;
         }
 
-        public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, Assumptions assumptions, ConstantReflectionProvider constantReflectionProvider,
+        public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, Assumptions assumptions, ConstantReflectionProvider constantReflection,
                         GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) {
-            this(metaAccess, stampProvider, assumptions, constantReflectionProvider, graphBuilderConfig, null, optimisticOpts);
+            this(metaAccess, stampProvider, assumptions, constantReflection, graphBuilderConfig, null, optimisticOpts);
         }
 
         @Override
@@ -174,6 +174,12 @@
             }
         }
 
+        private static class ExplodedLoopContext {
+            private BciBlock header;
+            private int targetPeelIteration;
+            private int peelIteration;
+        }
+
         public class BytecodeParser extends AbstractBytecodeParser<ValueNode, HIRFrameStateBuilder> implements GraphBuilderContext {
 
             private BciBlock[] loopHeaders;
@@ -196,6 +202,8 @@
 
             private FixedWithNextNode lastInstr;                 // the last instruction added
             private final boolean explodeLoops;
+            private Stack<ExplodedLoopContext> explodeLoopsContext;
+            private int nextPeelIteration = 1;
 
             public BytecodeParser(MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, int entryBCI) {
                 super(metaAccess, method, graphBuilderConfig, optimisticOpts);
@@ -272,15 +280,18 @@
                     }
 
                     currentBlock = blockMap.startBlock;
-                    blockMap.startBlock.entryState = frameState;
-                    if (blockMap.startBlock.isLoopHeader) {
+                    blockMap.startBlock.setEntryState(0, frameState);
+                    if (blockMap.startBlock.isLoopHeader && !explodeLoops) {
                         appendGoto(createTarget(blockMap.startBlock, frameState));
                     } else {
-                        blockMap.startBlock.firstInstruction = lastInstr;
+                        blockMap.startBlock.setFirstInstruction(0, lastInstr);
                     }
 
-                    for (BciBlock block : blockMap.getBlocks()) {
-                        processBlock(this, block);
+                    int index = 0;
+                    BciBlock[] blocks = blockMap.getBlocks();
+                    while (index < blocks.length) {
+                        BciBlock block = blocks[index];
+                        index = iterateBlock(blocks, block);
                     }
                     processBlock(this, returnBlock);
                     processBlock(this, unwindBlock);
@@ -290,6 +301,50 @@
                 }
             }
 
+            private int iterateBlock(BciBlock[] blocks, BciBlock block) {
+                if (block.isLoopHeader && this.explodeLoops) {
+                    return iterateExplodedLoopHeader(blocks, block);
+                } else {
+                    processBlock(this, block);
+                    return block.getId() + 1;
+                }
+            }
+
+            private int iterateExplodedLoopHeader(BciBlock[] blocks, BciBlock header) {
+                if (explodeLoopsContext == null) {
+                    explodeLoopsContext = new Stack<>();
+                }
+
+                ExplodedLoopContext context = new ExplodedLoopContext();
+                context.header = header;
+                context.peelIteration = this.getCurrentDimension();
+                context.targetPeelIteration = -1;
+                explodeLoopsContext.push(context);
+                Debug.dump(currentGraph, "before loop explosion " + context.peelIteration);
+
+                while (true) {
+
+                    processBlock(this, header);
+                    for (int j = header.getId() + 1; j <= header.loopEnd; ++j) {
+                        BciBlock block = blocks[j];
+                        iterateBlock(blocks, block);
+                    }
+
+                    if (context.targetPeelIteration != -1) {
+                        // We were reaching the backedge during explosion. Explode further.
+                        Debug.dump(currentGraph, "Before loop explosion " + context.targetPeelIteration);
+                        context.peelIteration = context.targetPeelIteration;
+                        context.targetPeelIteration = -1;
+                    } else {
+                        // We did not reach the backedge. Exit.
+                        Debug.dump(currentGraph, "after loop explosion " + context.peelIteration);
+                        break;
+                    }
+                }
+                explodeLoopsContext.pop();
+                return header.loopEnd + 1;
+            }
+
             private BciBlock returnBlock(int bci) {
                 if (returnBlock == null) {
                     returnBlock = new BciBlock();
@@ -452,7 +507,7 @@
 
             @Override
             protected ValueNode genLoadIndexed(ValueNode array, ValueNode index, Kind kind) {
-                return new LoadIndexedNode(array, index, kind);
+                return LoadIndexedNode.create(array, index, kind, metaAccess, constantReflection);
             }
 
             @Override
@@ -462,7 +517,7 @@
 
             @Override
             protected ValueNode genIntegerAdd(Kind kind, ValueNode x, ValueNode y) {
-                return new AddNode(x, y);
+                return AddNode.create(x, y);
             }
 
             @Override
@@ -578,17 +633,17 @@
 
             @Override
             protected ValueNode genObjectEquals(ValueNode x, ValueNode y) {
-                return ObjectEqualsNode.create(x, y, constantReflectionProvider);
+                return ObjectEqualsNode.create(x, y, constantReflection);
             }
 
             @Override
             protected ValueNode genIntegerEquals(ValueNode x, ValueNode y) {
-                return IntegerEqualsNode.create(x, y, constantReflectionProvider);
+                return IntegerEqualsNode.create(x, y, constantReflection);
             }
 
             @Override
             protected ValueNode genIntegerLessThan(ValueNode x, ValueNode y) {
-                return new IntegerLessThanNode(x, y);
+                return IntegerLessThanNode.create(x, y, constantReflection);
             }
 
             @Override
@@ -660,7 +715,7 @@
             protected void emitBoundsCheck(ValueNode index, ValueNode length) {
                 AbstractBeginNode trueSucc = currentGraph.add(new BeginNode());
                 BytecodeExceptionNode exception = currentGraph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
-                append(new IfNode(currentGraph.unique(new IntegerBelowNode(index, length)), trueSucc, exception, 0.99));
+                append(new IfNode(currentGraph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99));
                 lastInstr = trueSucc;
 
                 exception.setStateAfter(frameState.create(bci()));
@@ -669,7 +724,7 @@
 
             @Override
             protected ValueNode genArrayLength(ValueNode x) {
-                return new ArrayLengthNode(x);
+                return ArrayLengthNode.create(x, constantReflection);
             }
 
             @Override
@@ -977,7 +1032,7 @@
                 JsrScope scope = currentBlock.getJsrScope();
                 int retAddress = scope.nextReturnAddress();
                 ConstantNode returnBciNode = getJsrConstant(retAddress);
-                LogicNode guard = IntegerEqualsNode.create(local, returnBciNode, constantReflectionProvider);
+                LogicNode guard = IntegerEqualsNode.create(local, returnBciNode, constantReflection);
                 guard = currentGraph.unique(guard);
                 append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile));
                 if (!successor.getJsrScope().equals(scope.pop())) {
@@ -1058,7 +1113,7 @@
             }
 
             private Target checkLoopExit(FixedNode target, BciBlock targetBlock, HIRFrameStateBuilder state) {
-                if (currentBlock != null) {
+                if (currentBlock != null && !explodeLoops) {
                     long exits = currentBlock.loops & ~targetBlock.loops;
                     if (exits != 0) {
                         LoopExitNode firstLoopExit = null;
@@ -1089,7 +1144,7 @@
                         }
                         HIRFrameStateBuilder newState = state.copy();
                         for (BciBlock loop : exitLoops) {
-                            LoopBeginNode loopBegin = (LoopBeginNode) loop.firstInstruction;
+                            LoopBeginNode loopBegin = (LoopBeginNode) loop.getFirstInstruction(this.getCurrentDimension());
                             LoopExitNode loopExit = currentGraph.add(new LoopExitNode(loopBegin));
                             if (lastLoopExit != null) {
                                 lastLoopExit.setNext(loopExit);
@@ -1099,7 +1154,7 @@
                             }
                             lastLoopExit = loopExit;
                             Debug.log("Target %s (%s) Exits %s, scanning framestates...", targetBlock, target, loop);
-                            newState.insertLoopProxies(loopExit, (HIRFrameStateBuilder) loop.entryState);
+                            newState.insertLoopProxies(loopExit, (HIRFrameStateBuilder) loop.getEntryState(this.getCurrentDimension()));
                             loopExit.setStateAfter(newState.create(bci));
                         }
 
@@ -1124,53 +1179,88 @@
                 assert block != null && state != null;
                 assert !block.isExceptionEntry || state.stackSize() == 1;
 
-                if (block.firstInstruction == null) {
+                int operatingDimension = this.getCurrentDimension();
+                if (this.explodeLoops && this.explodeLoopsContext != null && !this.explodeLoopsContext.isEmpty()) {
+                    int i;
+                    for (i = explodeLoopsContext.size() - 1; i >= 0; --i) {
+                        ExplodedLoopContext context = explodeLoopsContext.elementAt(i);
+                        if (context.header == block) {
+
+                            // We have a hit on our current explosion context loop begin.
+                            if (context.targetPeelIteration == -1) {
+                                // This is the first hit => allocate a new dimension and at the same
+                                // time mark the context loop begin as hit during the current
+                                // iteration.
+                                context.targetPeelIteration = nextPeelIteration++;
+                                if (nextPeelIteration > GraalOptions.MaximumLoopExplosionCount.getValue()) {
+                                    throw new BailoutException("too many loop explosion interations - does the explosion not terminate?");
+                                }
+                            }
+
+                            // Operate on the target dimension.
+                            operatingDimension = context.targetPeelIteration;
+                            break;
+                        } else if (block.getId() > context.header.getId() && block.getId() <= context.header.loopEnd) {
+                            // We hit the range of this context.
+                            operatingDimension = context.peelIteration;
+                            break;
+                        }
+                    }
+
+                    if (i == -1) {
+                        // I did not find a dimension.
+                        operatingDimension = 0;
+                    }
+                }
+
+                if (block.getFirstInstruction(operatingDimension) == 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.
                      */
                     FixedNode targetNode;
-                    block.firstInstruction = currentGraph.add(new BeginNode());
-                    targetNode = block.firstInstruction;
+                    block.setFirstInstruction(operatingDimension, currentGraph.add(new BeginNode()));
+                    targetNode = block.getFirstInstruction(operatingDimension);
                     Target target = checkLoopExit(targetNode, block, state);
                     FixedNode result = target.fixed;
-                    block.entryState = target.state == state ? state.copy() : target.state;
-                    block.entryState.clearNonLiveLocals(block, liveness, true);
+                    AbstractFrameStateBuilder<?, ?> entryState = target.state == state ? state.copy() : target.state;
+                    block.setEntryState(operatingDimension, entryState);
+                    entryState.clearNonLiveLocals(block, liveness, true);
 
                     Debug.log("createTarget %s: first visit, result: %s", block, targetNode);
                     return result;
                 }
 
                 // We already saw this block before, so we have to merge states.
-                if (!((HIRFrameStateBuilder) block.entryState).isCompatibleWith(state)) {
+                if (!((HIRFrameStateBuilder) block.getEntryState(operatingDimension)).isCompatibleWith(state)) {
                     throw new BailoutException("stacks do not match; bytecodes would not verify");
                 }
 
-                if (block.firstInstruction instanceof LoopBeginNode) {
-                    assert block.isLoopHeader && currentBlock.getId() >= block.getId() : "must be backward branch";
+                if (block.getFirstInstruction(operatingDimension) instanceof LoopBeginNode) {
+                    assert this.explodeLoops || (block.isLoopHeader && currentBlock.getId() >= block.getId()) : "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;
+                    LoopBeginNode loopBegin = (LoopBeginNode) block.getFirstInstruction(operatingDimension);
                     Target target = checkLoopExit(currentGraph.add(new LoopEndNode(loopBegin)), block, state);
                     FixedNode result = target.fixed;
-                    ((HIRFrameStateBuilder) block.entryState).merge(loopBegin, target.state);
+                    ((HIRFrameStateBuilder) block.getEntryState(operatingDimension)).merge(loopBegin, target.state);
 
                     Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result);
                     return result;
                 }
                 assert currentBlock == null || currentBlock.getId() < block.getId() : "must not be backward branch";
-                assert block.firstInstruction.next() == null : "bytecodes already parsed for block";
+                assert block.getFirstInstruction(operatingDimension).next() == null : "bytecodes already parsed for block";
 
-                if (block.firstInstruction instanceof AbstractBeginNode && !(block.firstInstruction instanceof AbstractMergeNode)) {
+                if (block.getFirstInstruction(operatingDimension) instanceof AbstractBeginNode && !(block.getFirstInstruction(operatingDimension) instanceof AbstractMergeNode)) {
                     /*
                      * This is the second time we see this block. Create the actual MergeNode and
                      * the End Node for the already existing edge. For simplicity, we leave the
                      * placeholder in the graph and just append the new nodes after the placeholder.
                      */
-                    AbstractBeginNode placeholder = (AbstractBeginNode) block.firstInstruction;
+                    AbstractBeginNode placeholder = (AbstractBeginNode) block.getFirstInstruction(operatingDimension);
 
                     // The EndNode for the already existing edge.
                     AbstractEndNode end = currentGraph.add(new EndNode());
@@ -1188,16 +1278,16 @@
                     mergeNode.addForwardEnd(end);
                     mergeNode.setNext(next);
 
-                    block.firstInstruction = mergeNode;
+                    block.setFirstInstruction(operatingDimension, mergeNode);
                 }
 
-                AbstractMergeNode mergeNode = (AbstractMergeNode) block.firstInstruction;
+                AbstractMergeNode mergeNode = (AbstractMergeNode) block.getFirstInstruction(operatingDimension);
 
                 // The EndNode for the newly merged edge.
                 AbstractEndNode newEnd = currentGraph.add(new EndNode());
                 Target target = checkLoopExit(newEnd, block, state);
                 FixedNode result = target.fixed;
-                ((HIRFrameStateBuilder) block.entryState).merge(mergeNode, target.state);
+                ((HIRFrameStateBuilder) block.getEntryState(operatingDimension)).merge(mergeNode, target.state);
                 mergeNode.addForwardEnd(newEnd);
 
                 Debug.log("createTarget %s: merging state, result: %s", block, result);
@@ -1227,14 +1317,14 @@
 
             protected void processBlock(BytecodeParser parser, BciBlock block) {
                 // Ignore blocks that have no predecessors by the time their bytecodes are parsed
-                if (block == null || block.firstInstruction == null) {
+                if (block == null || block.getFirstInstruction(this.getCurrentDimension()) == null) {
                     Debug.log("Ignoring block %s", block);
                     return;
                 }
-                try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, block.firstInstruction, block.isLoopHeader)) {
+                try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, block.getFirstInstruction(this.getCurrentDimension()), block.isLoopHeader)) {
 
-                    lastInstr = block.firstInstruction;
-                    frameState = (HIRFrameStateBuilder) block.entryState;
+                    lastInstr = block.getFirstInstruction(this.getCurrentDimension());
+                    frameState = (HIRFrameStateBuilder) block.getEntryState(this.getCurrentDimension());
                     parser.setCurrentFrameState(frameState);
                     currentBlock = block;
 
@@ -1369,7 +1459,7 @@
 
             @Override
             protected void iterateBytecodesForBlock(BciBlock block) {
-                if (block.isLoopHeader) {
+                if (block.isLoopHeader && !explodeLoops) {
                     // Create the loop header block, which later will merge the backward branches of
                     // the loop.
                     AbstractEndNode preLoopEnd = currentGraph.add(new EndNode());
@@ -1380,7 +1470,7 @@
                     lastInstr = loopBegin;
 
                     // Create phi functions for all local variables and operand stack slots.
-                    frameState.insertLoopPhis(loopBegin);
+                    frameState.insertLoopPhis(liveness, block.loopId, loopBegin);
                     loopBegin.setStateAfter(frameState.create(block.startBci));
 
                     /*
@@ -1388,12 +1478,12 @@
                      * merge to the loop header. This ensures that the loop header has exactly one
                      * non-loop predecessor.
                      */
-                    block.firstInstruction = loopBegin;
+                    block.setFirstInstruction(this.getCurrentDimension(), loopBegin);
                     /*
                      * We need to preserve the frame state builder of the loop header so that we can
                      * merge values for phi functions, so make a copy of it.
                      */
-                    block.entryState = frameState.copy();
+                    block.setEntryState(this.getCurrentDimension(), frameState.copy());
 
                     Debug.log("  created loop header %s", loopBegin);
                 }
@@ -1569,9 +1659,22 @@
             }
 
             public void push(Kind kind, ValueNode value) {
+                assert kind == kind.getStackKind();
                 frameState.push(kind, value);
             }
 
+            private int getCurrentDimension() {
+                if (this.explodeLoopsContext == null || this.explodeLoopsContext.isEmpty()) {
+                    return 0;
+                } else {
+                    return this.explodeLoopsContext.peek().peelIteration;
+                }
+            }
+
+            public ConstantReflectionProvider getConstantReflection() {
+                return constantReflection;
+            }
+
         }
     }
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPlugins.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPlugins.java	Tue Feb 10 14:43:12 2015 +0100
@@ -94,6 +94,13 @@
         }
 
         /**
+         * @see #execute(GraphBuilderContext, InvocationPlugin, ValueNode[])
+         */
+        default boolean apply(GraphBuilderContext builder, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) {
+            throw invalidHandler(builder, arg1, arg2, arg3, arg4);
+        }
+
+        /**
          * Executes a given plugin against a set of invocation arguments by dispatching to the
          * plugin's {@code apply(...)} method that matches the number of arguments.
          *
@@ -111,6 +118,8 @@
                 return plugin.apply(builder, args[0], args[1]);
             } else if (args.length == 3) {
                 return plugin.apply(builder, args[0], args[1], args[2]);
+            } else if (args.length == 4) {
+                return plugin.apply(builder, args[0], args[1], args[2], args[3]);
             } else {
                 throw plugin.invalidHandler(builder, args);
             }
@@ -200,6 +209,17 @@
         }
 
         /**
+         * Registers a plugin for a method with 4 arguments.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void register4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) {
+            ResolvedJavaMethod method = arg1 == Receiver.class ? resolve(metaAccess, declaringClass, name, arg2, arg3, arg4) : resolve(metaAccess, declaringClass, name, arg1, arg2, arg3, arg4);
+            plugins.register(method, plugin);
+        }
+
+        /**
          * Resolves a method given a declaring class, name and parameter types.
          */
         public static ResolvedJavaMethod resolve(MetaAccessProvider metaAccess, Class<?> declaringClass, String name, Class<?>... parameterTypes) {
@@ -212,11 +232,27 @@
     }
 
     public static class InvocationPluginChecker {
-        static final Class<?>[] APPLY0 = {GraphBuilderContext.class};
-        static final Class<?>[] APPLY1 = {GraphBuilderContext.class, ValueNode.class};
-        static final Class<?>[] APPLY2 = {GraphBuilderContext.class, ValueNode.class, ValueNode.class};
-        static final Class<?>[] APPLY3 = {GraphBuilderContext.class, ValueNode.class, ValueNode.class, ValueNode.class};
-        static final Class<?>[][] SIGS = {APPLY0, APPLY1, APPLY2, APPLY3};
+        /**
+         * The set of all {@link InvocationPlugin#apply} method signatures.
+         */
+        static final Class<?>[][] SIGS;
+        static {
+            ArrayList<Class<?>[]> sigs = new ArrayList<>(5);
+            for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
+                if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
+                    Class<?>[] sig = method.getParameterTypes();
+                    assert sig[0] == GraphBuilderContext.class;
+                    assert Arrays.asList(Arrays.copyOfRange(sig, 1, sig.length)).stream().allMatch(c -> c == ValueNode.class);
+                    while (sigs.size() < sig.length) {
+                        sigs.add(null);
+                    }
+                    sigs.set(sig.length - 1, sig);
+                }
+            }
+            assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null),
+                            ValueNode.class.getSimpleName());
+            SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
+        }
 
         public static boolean check(ResolvedJavaMethod method, InvocationPlugin plugin) {
             int arguments = method.getSignature().getParameterCount(!method.isStatic());
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Tue Feb 10 14:43:12 2015 +0100
@@ -31,6 +31,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.debug.*;
+import com.oracle.graal.java.BciBlockMapping.LocalLiveness;
 import com.oracle.graal.java.GraphBuilderPlugins.ParameterPlugin;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
@@ -234,7 +235,7 @@
             return currentValue;
 
         } else if (currentValue != otherValue) {
-            assert !(block instanceof LoopBeginNode) : "Phi functions for loop headers are create eagerly for all locals and stack slots";
+            assert !(block instanceof LoopBeginNode) : "Phi functions for loop headers are create eagerly for changed locals and all stack slots";
             if (otherValue == null || otherValue.isDeleted() || currentValue.getKind() != otherValue.getKind()) {
                 return null;
             }
@@ -271,9 +272,11 @@
         }
     }
 
-    public void insertLoopPhis(LoopBeginNode loopBegin) {
+    public void insertLoopPhis(LocalLiveness liveness, int loopId, LoopBeginNode loopBegin) {
         for (int i = 0; i < localsSize(); i++) {
-            storeLocal(i, createLoopPhi(loopBegin, localAt(i)));
+            if (loopBegin.graph().isOSR() || liveness.localIsChangedInLoop(loopId, i)) {
+                storeLocal(i, createLoopPhi(loopBegin, localAt(i)));
+            }
         }
         for (int i = 0; i < stackSize(); i++) {
             storeStack(i, createLoopPhi(loopBegin, stackAt(i)));
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java	Tue Feb 10 14:43:12 2015 +0100
@@ -383,5 +383,11 @@
     public void reset() {
         asm.reset();
         compilationResult.reset();
+        if (exceptionInfoList != null) {
+            exceptionInfoList.clear();
+        }
+        if (dataCache != null) {
+            dataCache.clear();
+        }
     }
 }
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java	Tue Feb 10 14:43:12 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, 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
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.loop;
 
+import static com.oracle.graal.loop.MathUtil.*;
+
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.nodes.*;
@@ -29,10 +31,10 @@
 
 public class BasicInductionVariable extends InductionVariable {
 
-    private ValuePhiNode phi;
-    private ValueNode init;
-    private ValueNode rawStride;
-    private BinaryArithmeticNode<?> op;
+    private final ValuePhiNode phi;
+    private final ValueNode init;
+    private final ValueNode rawStride;
+    private final BinaryArithmeticNode<?> op;
 
     public BasicInductionVariable(LoopEx loop, ValuePhiNode phi, ValueNode init, ValueNode rawStride, BinaryArithmeticNode<?> op) {
         super(loop);
@@ -135,7 +137,7 @@
         if (!maxTripCount.stamp().isCompatible(stamp)) {
             maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph());
         }
-        return BinaryArithmeticNode.add(graph, BinaryArithmeticNode.mul(graph, stride, BinaryArithmeticNode.sub(graph, maxTripCount, ConstantNode.forIntegerStamp(stamp, 1, graph))), initNode);
+        return add(graph, mul(graph, stride, sub(graph, maxTripCount, ConstantNode.forIntegerStamp(stamp, 1, graph))), initNode);
     }
 
     @Override
@@ -145,7 +147,7 @@
         if (!maxTripCount.stamp().isCompatible(stamp)) {
             maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph());
         }
-        return BinaryArithmeticNode.add(graph(), BinaryArithmeticNode.mul(graph(), strideNode(), maxTripCount), initNode());
+        return add(graph(), mul(graph(), strideNode(), maxTripCount), initNode());
     }
 
     @Override
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/CountedLoopInfo.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/CountedLoopInfo.java	Tue Feb 10 14:43:12 2015 +0100
@@ -22,7 +22,7 @@
  */
 package com.oracle.graal.loop;
 
-import static com.oracle.graal.nodes.calc.BinaryArithmeticNode.*;
+import static com.oracle.graal.loop.MathUtil.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
@@ -55,20 +55,19 @@
     public ValueNode maxTripCountNode(boolean assumePositive) {
         StructuredGraph graph = iv.valueNode().graph();
         Stamp stamp = iv.valueNode().stamp();
-        BinaryArithmeticNode<?> range = BinaryArithmeticNode.sub(graph, end, iv.initNode());
+        ValueNode range = sub(graph, end, iv.initNode());
         if (oneOff) {
             if (iv.direction() == Direction.Up) {
-                range = BinaryArithmeticNode.add(graph, range, ConstantNode.forIntegerStamp(stamp, 1, graph));
+                range = add(graph, range, ConstantNode.forIntegerStamp(stamp, 1, graph));
             } else {
-                range = BinaryArithmeticNode.sub(graph, range, ConstantNode.forIntegerStamp(stamp, 1, graph));
+                range = sub(graph, range, ConstantNode.forIntegerStamp(stamp, 1, graph));
             }
         }
-        IntegerDivNode div = graph.add(new IntegerDivNode(range, iv.strideNode()));
-        graph.addBeforeFixed(loop.entryPoint(), div);
-        ConstantNode zero = ConstantNode.forIntegerStamp(stamp, 0, graph);
+        ValueNode div = divBefore(graph, loop.entryPoint(), range, iv.strideNode());
         if (assumePositive) {
             return div;
         }
+        ConstantNode zero = ConstantNode.forIntegerStamp(stamp, 0, graph);
         return graph.unique(new ConditionalNode(graph.unique(new IntegerLessThanNode(zero, div)), div, zero));
     }
 
@@ -144,14 +143,14 @@
         CompareNode cond; // we use a negated guard with a < condition to achieve a >=
         ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph);
         if (iv.direction() == Direction.Up) {
-            BinaryArithmeticNode<?> v1 = sub(graph, ConstantNode.forIntegerStamp(stamp, CodeUtil.maxValue(stamp.getBits()), graph), sub(graph, iv.strideNode(), one));
+            ValueNode v1 = sub(graph, ConstantNode.forIntegerStamp(stamp, CodeUtil.maxValue(stamp.getBits()), graph), sub(graph, iv.strideNode(), one));
             if (oneOff) {
                 v1 = sub(graph, v1, one);
             }
             cond = graph.unique(new IntegerLessThanNode(v1, end));
         } else {
             assert iv.direction() == Direction.Down;
-            BinaryArithmeticNode<?> v1 = add(graph, ConstantNode.forIntegerStamp(stamp, CodeUtil.minValue(stamp.getBits()), graph), sub(graph, one, iv.strideNode()));
+            ValueNode v1 = add(graph, ConstantNode.forIntegerStamp(stamp, CodeUtil.minValue(stamp.getBits()), graph), sub(graph, one, iv.strideNode()));
             if (oneOff) {
                 v1 = add(graph, v1, one);
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedInductionVariable.java	Tue Feb 10 14:43:12 2015 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, 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.loop;
+
+import com.oracle.graal.nodes.*;
+
+/**
+ * Base class of the derived induction variables.
+ */
+public abstract class DerivedInductionVariable extends InductionVariable {
+
+    protected final InductionVariable base;
+
+    public DerivedInductionVariable(LoopEx loop, InductionVariable base) {
+        super(loop);
+        this.base = base;
+    }
+
+    @Override
+    public StructuredGraph graph() {
+        return base.graph();
+    }
+
+    public InductionVariable getBase() {
+        return base;
+    }
+}
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedOffsetInductionVariable.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedOffsetInductionVariable.java	Tue Feb 10 14:43:12 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, 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
@@ -22,38 +22,29 @@
  */
 package com.oracle.graal.loop;
 
+import static com.oracle.graal.loop.MathUtil.*;
+
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 
-public class DerivedOffsetInductionVariable extends InductionVariable {
+public class DerivedOffsetInductionVariable extends DerivedInductionVariable {
 
-    private InductionVariable base;
-    private ValueNode offset;
-    private BinaryArithmeticNode<?> value;
+    private final ValueNode offset;
+    private final BinaryArithmeticNode<?> value;
 
     public DerivedOffsetInductionVariable(LoopEx loop, InductionVariable base, ValueNode offset, BinaryArithmeticNode<?> value) {
-        super(loop);
-        this.base = base;
+        super(loop, base);
         this.offset = offset;
         this.value = value;
     }
 
-    public InductionVariable getBase() {
-        return base;
-    }
-
     public ValueNode getOffset() {
         return offset;
     }
 
     @Override
-    public StructuredGraph graph() {
-        return base.graph();
-    }
-
-    @Override
     public Direction direction() {
         return base.direction();
     }
@@ -136,14 +127,14 @@
 
     private ValueNode op(ValueNode b, ValueNode o) {
         if (value instanceof AddNode) {
-            return BinaryArithmeticNode.add(graph(), b, o);
+            return add(graph(), b, o);
         }
         if (value instanceof SubNode) {
             if (base.valueNode() == value.getX()) {
-                return BinaryArithmeticNode.sub(graph(), b, o);
+                return sub(graph(), b, o);
             } else {
                 assert base.valueNode() == value.getY();
-                return BinaryArithmeticNode.sub(graph(), o, b);
+                return sub(graph(), o, b);
             }
         }
         throw GraalInternalError.shouldNotReachHere();
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedScaledInductionVariable.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedScaledInductionVariable.java	Tue Feb 10 14:43:12 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, 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
@@ -22,34 +22,37 @@
  */
 package com.oracle.graal.loop;
 
+import static com.oracle.graal.loop.MathUtil.*;
+
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.util.*;
 
-public class DerivedScaledInductionVariable extends InductionVariable {
+public class DerivedScaledInductionVariable extends DerivedInductionVariable {
 
-    private InductionVariable base;
-    private ValueNode scale;
-    private ValueNode value;
+    private final ValueNode scale;
+    private final ValueNode value;
 
     public DerivedScaledInductionVariable(LoopEx loop, InductionVariable base, ValueNode scale, ValueNode value) {
-        super(loop);
-        this.base = base;
+        super(loop, base);
         this.scale = scale;
         this.value = value;
     }
 
     public DerivedScaledInductionVariable(LoopEx loop, InductionVariable base, NegateNode value) {
-        super(loop);
-        this.base = base;
+        super(loop, base);
         this.scale = ConstantNode.forInt(-1, value.graph());
         this.value = value;
     }
 
+    public ValueNode getScale() {
+        return scale;
+    }
+
     @Override
-    public StructuredGraph graph() {
-        return base.graph();
+    public ValueNode valueNode() {
+        return value;
     }
 
     @Override
@@ -67,18 +70,13 @@
     }
 
     @Override
-    public ValueNode valueNode() {
-        return value;
-    }
-
-    @Override
     public ValueNode initNode() {
-        return BinaryArithmeticNode.mul(graph(), base.initNode(), scale);
+        return mul(graph(), base.initNode(), scale);
     }
 
     @Override
     public ValueNode strideNode() {
-        return BinaryArithmeticNode.mul(graph(), base.strideNode(), scale);
+        return mul(graph(), base.strideNode(), scale);
     }
 
     @Override
@@ -103,12 +101,12 @@
 
     @Override
     public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
-        return BinaryArithmeticNode.mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp, graph()));
+        return mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp, graph()));
     }
 
     @Override
     public ValueNode exitValueNode() {
-        return BinaryArithmeticNode.mul(graph(), base.exitValueNode(), scale);
+        return mul(graph(), base.exitValueNode(), scale);
     }
 
     @Override
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/InductionVariable.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/InductionVariable.java	Tue Feb 10 14:43:12 2015 +0100
@@ -87,7 +87,9 @@
 
     /**
      * Returns the extremum value of the induction variable. The extremum value is the value of the
-     * induction variable in the loop body of the last iteration.
+     * induction variable in the loop body of the last iteration, only taking into account the main
+     * loop limit test. It's possible for the loop to exit before this value if
+     * {@link CountedLoopInfo#isExactTripCount()} returns false for the containing loop.
      */
     public ValueNode extremumNode() {
         return extremumNode(false, valueNode().stamp());
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/InductionVariables.java	Tue Feb 10 11:15:39 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.loop;
-
-import static com.oracle.graal.graph.Node.*;
-
-import java.util.*;
-
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
-
-public class InductionVariables {
-
-    private final LoopEx loop;
-    private Map<Node, InductionVariable> ivs;
-
-    public InductionVariables(LoopEx loop) {
-        this.loop = loop;
-        ivs = newIdentityMap();
-        findDerived(findBasic());
-    }
-
-    public InductionVariable get(ValueNode v) {
-        return ivs.get(v);
-    }
-
-    private Collection<BasicInductionVariable> findBasic() {
-        List<BasicInductionVariable> bivs = new LinkedList<>();
-        LoopBeginNode loopBegin = loop.loopBegin();
-        AbstractEndNode forwardEnd = loopBegin.forwardEnd();
-        for (PhiNode phi : loopBegin.phis().filter(ValuePhiNode.class)) {
-            ValueNode backValue = phi.singleBackValue();
-            if (backValue == PhiNode.MULTIPLE_VALUES) {
-                continue;
-            }
-            ValueNode stride = addSub(backValue, phi);
-            if (stride != null) {
-                BasicInductionVariable biv = new BasicInductionVariable(loop, (ValuePhiNode) phi, phi.valueAt(forwardEnd), stride, (BinaryArithmeticNode<?>) backValue);
-                ivs.put(phi, biv);
-                bivs.add(biv);
-            }
-        }
-        return bivs;
-    }
-
-    private void findDerived(Collection<BasicInductionVariable> bivs) {
-        Queue<InductionVariable> scanQueue = new LinkedList<>(bivs);
-        while (!scanQueue.isEmpty()) {
-            InductionVariable baseIv = scanQueue.remove();
-            ValueNode baseIvNode = baseIv.valueNode();
-            for (ValueNode op : baseIvNode.usages().filter(ValueNode.class)) {
-                if (loop.isOutsideLoop(op)) {
-                    continue;
-                }
-                if (op.usages().count() == 1 && op.usages().first() == baseIvNode) {
-                    /*
-                     * This is just the base induction variable increment with no other uses so
-                     * don't bother reporting it.
-                     */
-                    continue;
-                }
-                InductionVariable iv = null;
-                ValueNode offset = addSub(op, baseIvNode);
-                ValueNode scale;
-                if (offset != null) {
-                    iv = new DerivedOffsetInductionVariable(loop, baseIv, offset, (BinaryArithmeticNode<?>) op);
-                } else if (op instanceof NegateNode) {
-                    iv = new DerivedScaledInductionVariable(loop, baseIv, (NegateNode) op);
-                } else if ((scale = mul(op, baseIvNode)) != null) {
-                    iv = new DerivedScaledInductionVariable(loop, baseIv, scale, op);
-                }
-
-                if (iv != null) {
-                    ivs.put(op, iv);
-                    scanQueue.offer(iv);
-                }
-            }
-        }
-    }
-
-    private ValueNode addSub(ValueNode op, ValueNode base) {
-        if (op.stamp() instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) {
-            BinaryArithmeticNode<?> aritOp = (BinaryArithmeticNode<?>) op;
-            if (aritOp.getX() == base && loop.isOutsideLoop(aritOp.getY())) {
-                return aritOp.getY();
-            } else if (aritOp.getY() == base && loop.isOutsideLoop(aritOp.getX())) {
-                return aritOp.getX();
-            }
-        }
-        return null;
-    }
-
-    private ValueNode mul(ValueNode op, ValueNode base) {
-        if (op instanceof MulNode) {
-            MulNode mul = (MulNode) op;
-            if (mul.getX() == base && loop.isOutsideLoop(mul.getY())) {
-                return mul.getY();
-            } else if (mul.getY() == base && loop.isOutsideLoop(mul.getX())) {
-                return mul.getX();
-            }
-        }
-        if (op instanceof LeftShiftNode) {
-            LeftShiftNode shift = (LeftShiftNode) op;
-            if (shift.getX() == base && shift.getY().isConstant()) {
-                return ConstantNode.forInt(1 << shift.getY().asJavaConstant().asInt(), base.graph());
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Deletes any nodes created within the scope of this object that have no usages.
-     */
-    public void deleteUnusedNodes() {
-        for (InductionVariable iv : ivs.values()) {
-            iv.deleteUnusedNodes();
-        }
-    }
-}
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue Feb 10 14:43:12 2015 +0100
@@ -22,11 +22,14 @@
  */
 package com.oracle.graal.loop;
 
+import static com.oracle.graal.graph.Node.*;
+
 import java.util.*;
 
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
@@ -44,7 +47,7 @@
     private LoopFragmentWhole whole;
     private CountedLoopInfo counted; // TODO (gd) detect
     private LoopsData data;
-    private InductionVariables ivs;
+    private Map<Node, InductionVariable> ivs;
 
     LoopEx(Loop<Block> loop, LoopsData data) {
         this.loop = loop;
@@ -252,19 +255,111 @@
         return LoopFragment.computeNodes(point.graph(), blocks, exits);
     }
 
-    public InductionVariables getInductionVariables() {
+    public Map<Node, InductionVariable> getInductionVariables() {
         if (ivs == null) {
-            ivs = new InductionVariables(this);
+            ivs = findInductionVariables(this);
         }
         return ivs;
     }
 
     /**
+     * Collect all the basic induction variables for the loop and the find any induction variables
+     * which are derived from the basic ones.
+     *
+     * @param loop
+     * @return a map from node to induction variable
+     */
+    private static Map<Node, InductionVariable> findInductionVariables(LoopEx loop) {
+        Map<Node, InductionVariable> ivs = newIdentityMap();
+
+        Queue<InductionVariable> scanQueue = new LinkedList<>();
+        LoopBeginNode loopBegin = loop.loopBegin();
+        AbstractEndNode forwardEnd = loopBegin.forwardEnd();
+        for (PhiNode phi : loopBegin.phis().filter(ValuePhiNode.class)) {
+            ValueNode backValue = phi.singleBackValue();
+            if (backValue == PhiNode.MULTIPLE_VALUES) {
+                continue;
+            }
+            ValueNode stride = addSub(loop, backValue, phi);
+            if (stride != null) {
+                BasicInductionVariable biv = new BasicInductionVariable(loop, (ValuePhiNode) phi, phi.valueAt(forwardEnd), stride, (BinaryArithmeticNode<?>) backValue);
+                ivs.put(phi, biv);
+                scanQueue.add(biv);
+            }
+        }
+
+        while (!scanQueue.isEmpty()) {
+            InductionVariable baseIv = scanQueue.remove();
+            ValueNode baseIvNode = baseIv.valueNode();
+            for (ValueNode op : baseIvNode.usages().filter(ValueNode.class)) {
+                if (loop.isOutsideLoop(op)) {
+                    continue;
+                }
+                if (op.usages().count() == 1 && op.usages().first() == baseIvNode) {
+                    /*
+                     * This is just the base induction variable increment with no other uses so
+                     * don't bother reporting it.
+                     */
+                    continue;
+                }
+                InductionVariable iv = null;
+                ValueNode offset = addSub(loop, op, baseIvNode);
+                ValueNode scale;
+                if (offset != null) {
+                    iv = new DerivedOffsetInductionVariable(loop, baseIv, offset, (BinaryArithmeticNode<?>) op);
+                } else if (op instanceof NegateNode) {
+                    iv = new DerivedScaledInductionVariable(loop, baseIv, (NegateNode) op);
+                } else if ((scale = mul(loop, op, baseIvNode)) != null) {
+                    iv = new DerivedScaledInductionVariable(loop, baseIv, scale, op);
+                }
+
+                if (iv != null) {
+                    ivs.put(op, iv);
+                    scanQueue.offer(iv);
+                }
+            }
+        }
+        return Collections.unmodifiableMap(ivs);
+    }
+
+    private static ValueNode addSub(LoopEx loop, ValueNode op, ValueNode base) {
+        if (op.stamp() instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) {
+            BinaryArithmeticNode<?> aritOp = (BinaryArithmeticNode<?>) op;
+            if (aritOp.getX() == base && loop.isOutsideLoop(aritOp.getY())) {
+                return aritOp.getY();
+            } else if (aritOp.getY() == base && loop.isOutsideLoop(aritOp.getX())) {
+                return aritOp.getX();
+            }
+        }
+        return null;
+    }
+
+    private static ValueNode mul(LoopEx loop, ValueNode op, ValueNode base) {
+        if (op instanceof MulNode) {
+            MulNode mul = (MulNode) op;
+            if (mul.getX() == base && loop.isOutsideLoop(mul.getY())) {
+                return mul.getY();
+            } else if (mul.getY() == base && loop.isOutsideLoop(mul.getX())) {
+                return mul.getX();
+            }
+        }
+        if (op instanceof LeftShiftNode) {
+            LeftShiftNode shift = (LeftShiftNode) op;
+            if (shift.getX() == base && shift.getY().isConstant()) {
+                return ConstantNode.forInt(1 << shift.getY().asJavaConstant().asInt(), base.graph());
+            }
+        }
+        return null;
+    }
+
+    /**
      * Deletes any nodes created within the scope of this object that have no usages.
      */
     public void deleteUnusedNodes() {
         if (ivs != null) {
-            ivs.deleteUnusedNodes();
+            for (InductionVariable iv : ivs.values()) {
+                iv.deleteUnusedNodes();
+            }
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/MathUtil.java	Tue Feb 10 14:43:12 2015 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, 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.loop;
+
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+
+/**
+ * Utility methods to perform integer math with some obvious constant folding first.
+ */
+public class MathUtil {
+    private static boolean isConstantOne(ValueNode v1) {
+        return v1.isConstant() && v1.stamp() instanceof IntegerStamp && v1.asJavaConstant().asLong() == 1;
+    }
+
+    private static boolean isConstantZero(ValueNode v1) {
+        return v1.isConstant() && v1.stamp() instanceof IntegerStamp && v1.asJavaConstant().asLong() == 0;
+    }
+
+    public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2) {
+        if (isConstantZero(v1)) {
+            return v2;
+        }
+        if (isConstantZero(v2)) {
+            return v1;
+        }
+        return BinaryArithmeticNode.add(graph, v1, v2);
+    }
+
+    public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2) {
+        if (isConstantOne(v1)) {
+            return v2;
+        }
+        if (isConstantOne(v2)) {
+            return v1;
+        }
+        return BinaryArithmeticNode.mul(graph, v1, v2);
+    }
+
+    public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2) {
+        if (isConstantZero(v2)) {
+            return v1;
+        }
+        return BinaryArithmeticNode.sub(graph, v1, v2);
+    }
+
+    public static ValueNode divBefore(StructuredGraph graph, FixedNode before, ValueNode dividend, ValueNode divisor) {
+        if (isConstantOne(divisor)) {
+            return dividend;
+        }
+        IntegerDivNode div = graph.add(new IntegerDivNode(dividend, divisor));
+        graph.addBeforeFixed(before, div);
+        return div;
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AddNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AddNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -39,6 +39,17 @@
         super(ArithmeticOpTable::getAdd, x, y);
     }
 
+    public static ValueNode create(ValueNode x, ValueNode y) {
+        BinaryOp<Add> op = ArithmeticOpTable.forStamp(x.stamp()).getAdd();
+        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+        if (tryConstantFold != null) {
+            return tryConstantFold;
+        } else {
+            return new AddNode(x, y);
+        }
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         ValueNode ret = super.canonical(tool, forX, forY);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BinaryArithmeticNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BinaryArithmeticNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -51,7 +51,7 @@
 
     protected final BinaryOp<OP> getOp(ValueNode forX, ValueNode forY) {
         ArithmeticOpTable table = ArithmeticOpTable.forStamp(forX.stamp());
-        assert table.toString().equals(ArithmeticOpTable.forStamp(forY.stamp()).toString());
+        assert table.equals(ArithmeticOpTable.forStamp(forY.stamp()));
         return getOp.apply(table);
     }
 
@@ -61,13 +61,21 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        if (forX.isConstant() && forY.isConstant()) {
-            Constant ret = getOp(forX, forY).foldConstant(forX.asConstant(), forY.asConstant());
-            return ConstantNode.forPrimitive(stamp(), ret);
+        ValueNode result = tryConstantFold(getOp(forX, forY), forX, forY, stamp());
+        if (result != null) {
+            return result;
         }
         return this;
     }
 
+    public static <OP> ConstantNode tryConstantFold(BinaryOp<OP> op, ValueNode forX, ValueNode forY, Stamp stamp) {
+        if (forX.isConstant() && forY.isConstant()) {
+            Constant ret = op.foldConstant(forX.asConstant(), forY.asConstant());
+            return ConstantNode.forPrimitive(stamp, ret);
+        }
+        return null;
+    }
+
     @Override
     public boolean inferStamp() {
         return updateStamp(getOp(getX(), getY()).foldStamp(getX().stamp(), getY().stamp()));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.nodes.calc;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.spi.*;
@@ -38,12 +39,37 @@
         assert y.stamp() instanceof IntegerStamp;
     }
 
+    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
+        LogicNode result = CompareNode.tryConstantFold(Condition.BT, x, y, constantReflection, false);
+        if (result != null) {
+            return result;
+        } else {
+            result = findSynonym(x, y);
+            if (result != null) {
+                return result;
+            }
+            return new IntegerBelowNode(x, y);
+        }
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         ValueNode result = super.canonical(tool, forX, forY);
         if (result != this) {
             return result;
         }
+        LogicNode synonym = findSynonym(forX, forY);
+        if (synonym != null) {
+            return synonym;
+        }
+        if (forX.isConstant() && forX.asJavaConstant().asLong() == 0) {
+            // 0 |<| y is the same as 0 != y
+            return new LogicNegationNode(CompareNode.createCompareNode(Condition.EQ, forX, forY));
+        }
+        return this;
+    }
+
+    private static LogicNode findSynonym(ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return LogicConstantNode.contradiction();
         } else if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
@@ -57,11 +83,7 @@
                 }
             }
         }
-        if (forX.isConstant() && forX.asJavaConstant().asLong() == 0) {
-            // 0 |<| y is the same as 0 != y
-            return new LogicNegationNode(CompareNode.createCompareNode(Condition.EQ, forX, forY));
-        }
-        return this;
+        return null;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -40,6 +40,19 @@
         assert !y.getKind().isNumericFloat() && y.getKind() != Kind.Object;
     }
 
+    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
+        LogicNode result = CompareNode.tryConstantFold(Condition.LT, x, y, constantReflection, false);
+        if (result != null) {
+            return result;
+        } else {
+            result = findSynonym(x, y);
+            if (result != null) {
+                return result;
+            }
+            return new IntegerLessThanNode(x, y);
+        }
+    }
+
     @Override
     protected ValueNode optimizeNormalizeCmp(Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) {
         PrimitiveConstant primitive = (PrimitiveConstant) constant;
@@ -63,6 +76,19 @@
         if (result != this) {
             return result;
         }
+        ValueNode synonym = findSynonym(forX, forY);
+        if (synonym != null) {
+            return synonym;
+        }
+        if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
+            if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
+                return new IntegerBelowNode(forX, forY);
+            }
+        }
+        return this;
+    }
+
+    private static LogicNode findSynonym(ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return LogicConstantNode.contradiction();
         } else if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
@@ -74,12 +100,7 @@
                 return LogicConstantNode.contradiction();
             }
         }
-        if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
-            if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
-                return new IntegerBelowNode(forX, forY);
-            }
-        }
-        return this;
+        return null;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -51,6 +51,14 @@
         this.array = array;
     }
 
+    public static ValueNode create(ValueNode forValue, ConstantReflectionProvider constantReflection) {
+        ValueNode length = readArrayLengthConstant(forValue, constantReflection);
+        if (length != null) {
+            return length;
+        }
+        return new ArrayLengthNode(forValue);
+    }
+
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
         ValueNode length = readArrayLength(forValue, tool.getConstantReflection());
         if (length != null) {
@@ -93,6 +101,10 @@
             // Ensure that any proxies on the original value end up on the length value
             return reproxyValue(originalArray, length);
         }
+        return readArrayLengthConstant(originalArray, constantReflection);
+    }
+
+    private static ValueNode readArrayLengthConstant(ValueNode originalArray, ConstantReflectionProvider constantReflection) {
         ValueNode array = GraphUtil.unproxify(originalArray);
         if (constantReflection != null && array.isConstant() && !array.isNullConstant()) {
             JavaConstant constantValue = array.asJavaConstant();
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Tue Feb 10 14:43:12 2015 +0100
@@ -48,6 +48,14 @@
         this(createStamp(array, elementKind), array, index, elementKind);
     }
 
+    public static ValueNode create(ValueNode array, ValueNode index, Kind elementKind, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {
+        ValueNode constant = tryConstantFold(array, index, metaAccess, constantReflection);
+        if (constant != null) {
+            return constant;
+        }
+        return new LoadIndexedNode(array, index, elementKind);
+    }
+
     protected LoadIndexedNode(Stamp stamp, ValueNode array, ValueNode index, Kind elementKind) {
         super(stamp, array, index, elementKind);
     }
@@ -79,15 +87,23 @@
     }
 
     public Node canonical(CanonicalizerTool tool) {
-        if (array().isConstant() && !array().isNullConstant() && index().isConstant()) {
-            JavaConstant arrayConstant = array().asJavaConstant();
+        ValueNode constant = tryConstantFold(array(), index(), tool.getMetaAccess(), tool.getConstantReflection());
+        if (constant != null) {
+            return constant;
+        }
+        return this;
+    }
+
+    private static ValueNode tryConstantFold(ValueNode array, ValueNode index, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {
+        if (array.isConstant() && !array.isNullConstant() && index.isConstant()) {
+            JavaConstant arrayConstant = array.asJavaConstant();
             if (arrayConstant != null) {
-                JavaConstant constant = tool.getConstantReflection().readConstantArrayElement(arrayConstant, index().asJavaConstant().asInt());
+                JavaConstant constant = constantReflection.readConstantArrayElement(arrayConstant, index.asJavaConstant().asInt());
                 if (constant != null) {
-                    return ConstantNode.forConstant(constant, tool.getMetaAccess());
+                    return ConstantNode.forConstant(constant, metaAccess);
                 }
             }
         }
-        return this;
+        return null;
     }
 }
--- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/DerivedOptionValue.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/DerivedOptionValue.java	Tue Feb 10 14:43:12 2015 +0100
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.options;
 
+import java.io.*;
 import java.util.function.*;
 
 import com.oracle.graal.options.OptionValue.OverrideScope;
@@ -31,10 +32,13 @@
  */
 public class DerivedOptionValue<T> {
 
+    public interface OptionSupplier<T> extends Supplier<T>, Serializable {
+    }
+
     private final T initialValue;
-    private final Supplier<T> supplier;
+    private final OptionSupplier<T> supplier;
 
-    public DerivedOptionValue(Supplier<T> supplier) {
+    public DerivedOptionValue(OptionSupplier<T> supplier) {
         this.supplier = supplier;
         assert OptionValue.getOverrideScope() == null : "derived option value should be initialized outside any override scope";
         this.initialValue = createValue();
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue Feb 10 14:43:12 2015 +0100
@@ -633,9 +633,9 @@
             return graph;
         }
 
-        protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, Assumptions assumptions, ConstantReflectionProvider constantReflectionProvider,
+        protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, Assumptions assumptions, ConstantReflectionProvider constantReflection,
                         GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts) {
-            return new GraphBuilderPhase.Instance(metaAccess, stampProvider, assumptions, constantReflectionProvider, graphBuilderConfig, optimisticOpts);
+            return new GraphBuilderPhase.Instance(metaAccess, stampProvider, assumptions, constantReflection, graphBuilderConfig, optimisticOpts);
         }
 
         protected void afterParsing(StructuredGraph graph) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue Feb 10 14:43:12 2015 +0100
@@ -58,6 +58,7 @@
 import com.oracle.graal.truffle.nodes.frame.*;
 import com.oracle.graal.truffle.nodes.frame.NewFrameNode.VirtualOnlyInstanceNode;
 import com.oracle.graal.truffle.phases.*;
+import com.oracle.graal.truffle.substitutions.*;
 import com.oracle.graal.virtual.phases.ea.*;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -148,12 +149,17 @@
         public boolean apply(GraphBuilderContext builder, ValueNode receiver, ResolvedJavaField field) {
             if (receiver.isConstant()) {
                 JavaConstant asJavaConstant = receiver.asJavaConstant();
-                JavaConstant result = providers.getConstantReflection().readConstantFieldValue(field, asJavaConstant);
-                if (result != null) {
-                    ConstantNode constantNode = builder.append(ConstantNode.forConstant(result, providers.getMetaAccess()));
-                    builder.push(constantNode.getKind().getStackKind(), constantNode);
-                    return true;
-                }
+                return tryConstantFold(builder, field, asJavaConstant);
+            }
+            return false;
+        }
+
+        private boolean tryConstantFold(GraphBuilderContext builder, ResolvedJavaField field, JavaConstant asJavaConstant) {
+            JavaConstant result = providers.getConstantReflection().readConstantFieldValue(field, asJavaConstant);
+            if (result != null) {
+                ConstantNode constantNode = builder.append(ConstantNode.forConstant(result, providers.getMetaAccess()));
+                builder.push(constantNode.getKind().getStackKind(), constantNode);
+                return true;
             }
             return false;
         }
@@ -164,7 +170,7 @@
                 builder.push(trueNode.getKind().getStackKind(), trueNode);
                 return true;
             }
-            return false;
+            return tryConstantFold(builder, staticField, null);
         }
     }
 
@@ -212,6 +218,7 @@
         for (GraphBuilderPluginsProvider p : sl) {
             p.registerPlugins(providers.getMetaAccess(), plugins);
         }
+        TruffleGraphBuilderPlugins.registerPlugins(providers.getMetaAccess(), plugins);
         new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), new Assumptions(false), providers.getConstantReflection(), newConfig, plugins,
                         TruffleCompilerImpl.Optimizations).apply(graph);
         Debug.dump(graph, "After FastPE");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Tue Feb 10 14:43:12 2015 +0100
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015, 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.truffle.substitutions;
+
+import static java.lang.Character.*;
+
+import java.util.concurrent.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.calc.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.java.*;
+import com.oracle.graal.java.GraphBuilderPlugins.InvocationPlugin;
+import com.oracle.graal.java.GraphBuilderPlugins.Registration;
+import com.oracle.graal.java.GraphBuilderPlugins.Registration.Receiver;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.truffle.*;
+import com.oracle.graal.truffle.nodes.*;
+import com.oracle.graal.truffle.nodes.frame.*;
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+
+/**
+ * Provider of {@link GraphBuilderPlugin}s for Truffle classes.
+ */
+public class TruffleGraphBuilderPlugins {
+    public static void registerPlugins(MetaAccessProvider metaAccess, GraphBuilderPlugins plugins) {
+
+        // CompilerDirectives.class
+        Registration r = new Registration(plugins, metaAccess, CompilerDirectives.class);
+        r.register0("inInterpreter", new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder) {
+                builder.push(Kind.Boolean.getStackKind(), builder.append(ConstantNode.forBoolean(false)));
+                return true;
+            }
+        });
+        r.register0("inCompiledCode", new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder) {
+                builder.push(Kind.Boolean.getStackKind(), builder.append(ConstantNode.forBoolean(true)));
+                return true;
+            }
+        });
+        r.register0("transferToInterpreter", new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder) {
+                builder.append(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
+                return true;
+            }
+        });
+        r.register0("transferToInterpreterAndInvalidate", new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder) {
+                builder.append(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
+                return true;
+            }
+        });
+        r.register1("interpreterOnly", Runnable.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode arg) {
+                return true;
+            }
+        });
+        r.register1("interpreterOnly", Callable.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode arg) {
+                return true;
+            }
+        });
+        r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode probability, ValueNode condition) {
+                builder.append(new BranchProbabilityNode(probability, condition));
+                return true;
+            }
+        });
+        r.register1("bailout", String.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode message) {
+                if (message.isConstant()) {
+                    throw new BailoutException(message.asConstant().toValueString());
+                }
+                throw new BailoutException("bailout (message is not compile-time constant, so no additional information is available)");
+            }
+        });
+        r.register1("isCompilationConstant", Object.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode value) {
+                if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) {
+                    builder.push(Kind.Boolean.getStackKind(), builder.append(ConstantNode.forBoolean(true)));
+                    return true;
+                }
+                return false;
+            }
+        });
+        r.register1("materialize", Object.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode value) {
+                builder.append(new ForceMaterializeNode(value));
+                return true;
+            }
+        });
+
+        // OptimizedCallTarget.class
+        r = new Registration(plugins, metaAccess, OptimizedCallTarget.class);
+        r.register2("createFrame", FrameDescriptor.class, Object[].class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode arg1, ValueNode arg2) {
+                builder.push(Kind.Object, builder.append(new NewFrameNode(StampFactory.exactNonNull(metaAccess.lookupJavaType(FrameWithoutBoxing.class)), arg1, arg2)));
+                return true;
+            }
+        });
+
+        // FrameWithoutBoxing.class
+        r = new Registration(plugins, metaAccess, FrameWithoutBoxing.class);
+        r.register1("materialize", Receiver.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode frame) {
+                builder.append(new MaterializeFrameNode(frame));
+                return true;
+            }
+        });
+        r.register4("unsafeCast", Object.class, Class.class, boolean.class, boolean.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode object, ValueNode clazz, ValueNode condition, ValueNode nonNull) {
+                if (clazz.isConstant() && nonNull.isConstant()) {
+                    ConstantReflectionProvider constantReflection = builder.getConstantReflection();
+                    ResolvedJavaType javaType = constantReflection.asJavaType(clazz.asConstant());
+                    if (javaType == null) {
+                        builder.push(Kind.Object, object);
+                    } else {
+                        Stamp piStamp = StampFactory.declaredTrusted(javaType, nonNull.asJavaConstant().asInt() != 0);
+                        ConditionAnchorNode valueAnchorNode = builder.append(new ConditionAnchorNode(CompareNode.createCompareNode(object.graph(), Condition.EQ, condition,
+                                        ConstantNode.forBoolean(true, object.graph()))));
+                        PiNode piCast = builder.append(new PiNode(object, piStamp, valueAnchorNode));
+                        builder.push(Kind.Object, piCast);
+                    }
+                    return true;
+                }
+                // TODO: should we throw GraalInternalError.shouldNotReachHere() here?
+                return false;
+            }
+        });
+        for (Kind kind : new Kind[]{Kind.Boolean, Kind.Byte, Kind.Int, Kind.Long, Kind.Float, Kind.Double, Kind.Object}) {
+            String kindName = kind.getJavaName();
+            kindName = toUpperCase(kindName.charAt(0)) + kindName.substring(1);
+            String getName = "unsafeGet" + kindName;
+            String putName = "unsafePut" + kindName;
+            r.register4(getName, Object.class, long.class, boolean.class, Object.class, new CustomizedUnsafeLoadPlugin(kind));
+            r.register4(putName, Object.class, long.class, kind == Kind.Object ? Object.class : kind.toJavaClass(), Object.class, new CustomizedUnsafeStorePlugin(kind));
+        }
+    }
+
+    static class CustomizedUnsafeLoadPlugin implements InvocationPlugin {
+
+        private final Kind returnKind;
+
+        public CustomizedUnsafeLoadPlugin(Kind returnKind) {
+            this.returnKind = returnKind;
+        }
+
+        public boolean apply(GraphBuilderContext builder, ValueNode object, ValueNode offset, ValueNode condition, ValueNode location) {
+            if (location.isConstant()) {
+                LocationIdentity locationIdentity;
+                if (location.isNullConstant()) {
+                    locationIdentity = LocationIdentity.ANY_LOCATION;
+                } else {
+                    locationIdentity = ObjectLocationIdentity.create(location.asJavaConstant());
+                }
+                CompareNode compare = builder.append(CompareNode.createCompareNode(Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph())));
+                builder.push(returnKind.getStackKind(), builder.append(new UnsafeLoadNode(object, offset, returnKind, locationIdentity, compare)));
+                return true;
+            }
+            // TODO: should we throw GraalInternalError.shouldNotReachHere() here?
+            return false;
+        }
+    }
+
+    static class CustomizedUnsafeStorePlugin implements InvocationPlugin {
+
+        private final Kind kind;
+
+        public CustomizedUnsafeStorePlugin(Kind kind) {
+            this.kind = kind;
+        }
+
+        public boolean apply(GraphBuilderContext builder, ValueNode object, ValueNode offset, ValueNode value, ValueNode location) {
+            ValueNode locationArgument = location;
+            if (locationArgument.isConstant()) {
+                LocationIdentity locationIdentity;
+                if (locationArgument.isNullConstant()) {
+                    locationIdentity = LocationIdentity.ANY_LOCATION;
+                } else {
+                    locationIdentity = ObjectLocationIdentity.create(locationArgument.asJavaConstant());
+                }
+
+                builder.append(new UnsafeStoreNode(object, offset, value, kind, locationIdentity, null));
+                return true;
+            }
+            // TODO: should we throw GraalInternalError.shouldNotReachHere() here?
+            return false;
+        }
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPluginsProvider.java	Tue Feb 10 11:15:39 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2015, 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.truffle.substitutions;
-
-import java.util.concurrent.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.runtime.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.java.*;
-import com.oracle.graal.java.GraphBuilderPlugins.InvocationPlugin;
-import com.oracle.graal.java.GraphBuilderPlugins.Registration;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.truffle.*;
-import com.oracle.graal.truffle.nodes.frame.*;
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-
-/**
- * Provider of {@link GraphBuilderPlugin}s for Truffle classes.
- */
-@ServiceProvider(GraphBuilderPluginsProvider.class)
-public class TruffleGraphBuilderPluginsProvider implements GraphBuilderPluginsProvider {
-    public void registerPlugins(MetaAccessProvider metaAccess, GraphBuilderPlugins plugins) {
-
-        Registration r2 = new Registration(plugins, metaAccess, OptimizedCallTarget.class);
-        r2.register2("createFrame", FrameDescriptor.class, Object[].class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode arg1, ValueNode arg2) {
-                builder.push(Kind.Object, builder.append(new NewFrameNode(StampFactory.exactNonNull(metaAccess.lookupJavaType(FrameWithoutBoxing.class)), arg1, arg2)));
-                return true;
-            }
-        });
-
-        Registration r = new Registration(plugins, metaAccess, CompilerDirectives.class);
-        r.register0("inInterpreter", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder) {
-                builder.push(Kind.Boolean, builder.append(ConstantNode.forBoolean(false)));
-                return true;
-            }
-        });
-        r.register0("inCompiledCode", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder) {
-                builder.push(Kind.Boolean, builder.append(ConstantNode.forBoolean(true)));
-                return true;
-            }
-        });
-        r.register0("transferToInterpreter", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder) {
-                builder.append(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter));
-                return true;
-            }
-        });
-        r.register0("transferToInterpreterAndInvalidate", new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder) {
-                builder.append(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter));
-                return true;
-            }
-        });
-        r.register1("interpreterOnly", Runnable.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode arg) {
-                return true;
-            }
-        });
-        r.register1("interpreterOnly", Callable.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode arg) {
-                return true;
-            }
-        });
-        r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode probability, ValueNode condition) {
-                builder.append(new BranchProbabilityNode(probability, condition));
-                return true;
-            }
-        });
-        r.register1("bailout", String.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode message) {
-                if (message.isConstant()) {
-                    throw new BailoutException(message.asConstant().toValueString());
-                }
-                throw new BailoutException("bailout (message is not compile-time constant, so no additional information is available)");
-            }
-        });
-        r.register1("isCompilationConstant", Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode value) {
-                if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) {
-                    builder.push(Kind.Boolean, builder.append(ConstantNode.forBoolean(true)));
-                    return true;
-                }
-                return false;
-            }
-        });
-        r.register1("materialize", Object.class, new InvocationPlugin() {
-            public boolean apply(GraphBuilderContext builder, ValueNode value) {
-                builder.append(new ForceMaterializeNode(value));
-                return true;
-            }
-        });
-    }
-}
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TruffleToolTest.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TruffleToolTest.java	Tue Feb 10 14:43:12 2015 +0100
@@ -29,7 +29,7 @@
 import com.oracle.truffle.api.instrument.*;
 
 /**
- * Test the basic life cycle properties shared by all instances of {@link TruffleTool}.
+ * Test the basic life cycle properties shared by all instances of {@link InstrumentationTool}.
  */
 public class TruffleToolTest {
 
@@ -110,7 +110,7 @@
         tool.dispose();
     }
 
-    private static final class DummyTruffleTool extends TruffleTool {
+    private static final class DummyTruffleTool extends InstrumentationTool {
 
         @Override
         protected boolean internalInstall() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java	Tue Feb 10 14:43:12 2015 +0100
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.instrument;
+
+/**
+ * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language
+ * program execution.
+ * <p>
+ * Tools share a common <em>life cycle</em>:
+ * <ul>
+ * <li>A newly created tool is inert until {@linkplain #install() installed}.</li>
+ * <li>An installed tool becomes <em>enabled</em> and immediately begins installing
+ * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from
+ * those instruments</li>
+ * <li>A tool may only be installed once.</li>
+ * <li>It should be possible to install multiple instances of a tool, possibly (but not necessarily)
+ * configured differently with respect to what data is being collected.</li>
+ * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled}
+ * arbitrarily.</li>
+ * <li>A disabled tool:
+ * <ul>
+ * <li>Collects no data;</li>
+ * <li>Retains existing AST instrumentation;</li>
+ * <li>Continues to instrument newly created ASTs; and</li>
+ * <li>Retains previously collected data.</li>
+ * </ul>
+ * </li>
+ * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool
+ * installed but with all previously collected data removed.</li>
+ * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not
+ * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data persists.</li>
+ * </ul>
+ * <p>
+ * Tool-specific methods that access data collected by the tool should:
+ * <ul>
+ * <li>Return modification-safe representations of the data; and</li>
+ * <li>Not change the state of the data.</li>
+ * </ul>
+ * <b>Note:</b><br>
+ * Tool installation is currently <em>global</em> to the Truffle Execution environment. When
+ * language-agnostic management of individual execution environments is added to the platform,
+ * installation will be (optionally) specific to a single execution environment.
+ */
+public abstract class InstrumentationTool {
+    // TODO (mlvdv) still thinking about the most appropriate name for this class of tools
+
+    private enum ToolState {
+
+        /** Not yet installed, inert. */
+        UNINSTALLED,
+
+        /** Installed, collecting data. */
+        ENABLED,
+
+        /** Installed, not collecting data. */
+        DISABLED,
+
+        /** Was installed, but now removed, inactive, and no longer usable. */
+        DISPOSED;
+    }
+
+    private ToolState toolState = ToolState.UNINSTALLED;
+
+    protected InstrumentationTool() {
+    }
+
+    /**
+     * Connect the tool to some part of the Truffle runtime, and enable data collection to start.
+     * Instrumentation will only be added to subsequently created ASTs.
+     *
+     * @throws IllegalStateException if the tool has previously been installed.
+     */
+    public final void install() {
+        checkUninstalled();
+        if (internalInstall()) {
+            toolState = ToolState.ENABLED;
+        }
+    }
+
+    /**
+     * @return whether the tool is currently collecting data.
+     */
+    public final boolean isEnabled() {
+        return toolState == ToolState.ENABLED;
+    }
+
+    /**
+     * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not
+     * collecting data, but keeping data already collected).
+     *
+     * @throws IllegalStateException if not yet installed or disposed.
+     */
+    public final void setEnabled(boolean isEnabled) {
+        checkInstalled();
+        internalSetEnabled(isEnabled);
+        toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED;
+    }
+
+    /**
+     * Clears any data already collected, but otherwise does not change the state of the tool.
+     *
+     * @throws IllegalStateException if not yet installed or disposed.
+     */
+    public final void reset() {
+        checkInstalled();
+        internalReset();
+    }
+
+    /**
+     * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data already
+     * collected.
+     *
+     * @throws IllegalStateException if not yet installed or disposed.
+     */
+    public final void dispose() {
+        checkInstalled();
+        internalDispose();
+        toolState = ToolState.DISPOSED;
+    }
+
+    /**
+     * @return whether the installation succeeded.
+     */
+    protected abstract boolean internalInstall();
+
+    /**
+     * No subclass action required.
+     *
+     * @param isEnabled
+     */
+    protected void internalSetEnabled(boolean isEnabled) {
+    }
+
+    protected abstract void internalReset();
+
+    protected abstract void internalDispose();
+
+    /**
+     * Ensure that the tool is currently installed.
+     *
+     * @throws IllegalStateException
+     */
+    private void checkInstalled() throws IllegalStateException {
+        if (toolState == ToolState.UNINSTALLED) {
+            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed");
+        }
+        if (toolState == ToolState.DISPOSED) {
+            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed");
+        }
+    }
+
+    /**
+     * Ensure that the tool has not yet been installed.
+     *
+     * @throws IllegalStateException
+     */
+    private void checkUninstalled() {
+        if (toolState != ToolState.UNINSTALLED) {
+            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed");
+        }
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/TruffleTool.java	Tue Feb 10 11:15:39 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * Copyright (c) 2015, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.instrument;
-
-/**
- * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language
- * program execution.
- * <p>
- * Tools share a common <em>life cycle</em>:
- * <ul>
- * <li>A newly created tool is inert until {@linkplain #install() installed}.</li>
- * <li>An installed tool becomes <em>enabled</em> and immediately begins installing
- * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from
- * those instruments</li>
- * <li>A tool may only be installed once.</li>
- * <li>It should be possible to install multiple instances of a tool, possibly (but not necessarily)
- * configured differently with respect to what data is being collected.</li>
- * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled}
- * arbitrarily.</li>
- * <li>A disabled tool:
- * <ul>
- * <li>Collects no data;</li>
- * <li>Retains existing AST instrumentation;</li>
- * <li>Continues to instrument newly created ASTs; and</li>
- * <li>Retains previously collected data.</li>
- * </ul>
- * </li>
- * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool
- * installed but with all previously collected data removed.</li>
- * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not
- * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data persists.</li>
- * </ul>
- * <p>
- * Tool-specific methods that access data collected by the tool should:
- * <ul>
- * <li>Return modification-safe representations of the data; and</li>
- * <li>Not change the state of the data.</li>
- * </ul>
- * <b>Note:</b><br>
- * Tool installation is currently <em>global</em> to the Truffle Execution environment. When
- * language-agnostic management of individual execution environments is added to the platform,
- * installation will be (optionally) specific to a single execution environment.
- */
-public abstract class TruffleTool {
-
-    private enum ToolState {
-
-        /** Not yet installed, inert. */
-        UNINSTALLED,
-
-        /** Installed, collecting data. */
-        ENABLED,
-
-        /** Installed, not collecting data. */
-        DISABLED,
-
-        /** Was installed, but now removed, inactive, and no longer usable. */
-        DISPOSED;
-    }
-
-    private ToolState toolState = ToolState.UNINSTALLED;
-
-    protected TruffleTool() {
-    }
-
-    /**
-     * Connect the tool to some part of the Truffle runtime, and enable data collection to start.
-     * Instrumentation will only be added to subsequently created ASTs.
-     *
-     * @throws IllegalStateException if the tool has previously been installed.
-     */
-    public final void install() {
-        checkUninstalled();
-        if (internalInstall()) {
-            toolState = ToolState.ENABLED;
-        }
-    }
-
-    /**
-     * @return whether the tool is currently collecting data.
-     */
-    public final boolean isEnabled() {
-        return toolState == ToolState.ENABLED;
-    }
-
-    /**
-     * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not
-     * collecting data, but keeping data already collected).
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void setEnabled(boolean isEnabled) {
-        checkInstalled();
-        internalSetEnabled(isEnabled);
-        toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED;
-    }
-
-    /**
-     * Clears any data already collected, but otherwise does not change the state of the tool.
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void reset() {
-        checkInstalled();
-        internalReset();
-    }
-
-    /**
-     * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data already
-     * collected.
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void dispose() {
-        checkInstalled();
-        internalDispose();
-        toolState = ToolState.DISPOSED;
-    }
-
-    /**
-     * @return whether the installation succeeded.
-     */
-    protected abstract boolean internalInstall();
-
-    /**
-     * No subclass action required.
-     *
-     * @param isEnabled
-     */
-    protected void internalSetEnabled(boolean isEnabled) {
-    }
-
-    protected abstract void internalReset();
-
-    protected abstract void internalDispose();
-
-    /**
-     * Ensure that the tool is currently installed.
-     *
-     * @throws IllegalStateException
-     */
-    private void checkInstalled() throws IllegalStateException {
-        if (toolState == ToolState.UNINSTALLED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed");
-        }
-        if (toolState == ToolState.DISPOSED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed");
-        }
-    }
-
-    /**
-     * Ensure that the tool has not yet been installed.
-     *
-     * @throws IllegalStateException
-     */
-    private void checkUninstalled() {
-        if (toolState != ToolState.UNINSTALLED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed");
-        }
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java	Tue Feb 10 14:43:12 2015 +0100
@@ -38,15 +38,15 @@
 import com.oracle.truffle.api.source.*;
 
 /**
- * A {@link TruffleTool} that counts interpreter <em>execution calls</em> to AST nodes that hold a
- * specified {@linkplain SyntaxTag tag}, tabulated by source and line number associated with each
- * node. Tags are presumed to be applied external to the tool. If no tag is specified,
+ * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes that
+ * hold a specified {@linkplain SyntaxTag tag}, tabulated by source and line number associated with
+ * each node. Tags are presumed to be applied external to the tool. If no tag is specified,
  * {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to conventional
  * behavior for code coverage tools.
  * <p>
  * <b>Tool Life Cycle</b>
  * <p>
- * See {@link TruffleTool} for the life cycle common to all such tools.
+ * See {@link InstrumentationTool} for the life cycle common to all such tools.
  * <p>
  * <b>Execution Counts</b>
  * <p>
@@ -72,7 +72,7 @@
  * @see Instrument
  * @see SyntaxTag
  */
-public final class CoverageTracker extends TruffleTool {
+public final class CoverageTracker extends InstrumentationTool {
 
     /** Counting data. */
     private final Map<LineLocation, CoverageCounter> counters = new HashMap<>();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/LineToProbesMap.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/LineToProbesMap.java	Tue Feb 10 14:43:12 2015 +0100
@@ -32,10 +32,10 @@
 import com.oracle.truffle.api.source.*;
 
 /**
- * A {@link TruffleTool} that builds a map of every {@Probe} attached to some AST, indexed
- * by {@link Source} and line number.
+ * An {@link InstrumentationTool} that builds a map of every {@Probe} attached to some AST,
+ * indexed by {@link Source} and line number.
  */
-public final class LineToProbesMap extends TruffleTool {
+public final class LineToProbesMap extends InstrumentationTool {
 
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java	Tue Feb 10 11:15:39 2015 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java	Tue Feb 10 14:43:12 2015 +0100
@@ -36,13 +36,14 @@
 import com.oracle.truffle.api.nodes.Node.Child;
 
 /**
- * A {@link TruffleTool} that counts interpreter <em>execution calls</em> to AST nodes, tabulated by
- * the type of called nodes; counting can be enabled <em>all</em> nodes or restricted to nodes with
- * a specified {@linkplain SyntaxTag tag} that is presumed to be applied external to the tool.
+ * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes,
+ * tabulated by the type of called nodes; counting can be enabled <em>all</em> nodes or restricted
+ * to nodes with a specified {@linkplain SyntaxTag tag} that is presumed to be applied external to
+ * the tool.
  * <p>
  * <b>Tool Life Cycle</b>
  * <p>
- * See {@link TruffleTool} for the life cycle common to all such tools.
+ * See {@link InstrumentationTool} for the life cycle common to all such tools.
  * </p>
  * <b>Execution Counts</b>
  * <p>
@@ -79,7 +80,7 @@
  * @see SyntaxTag
  * @see ProbeFailure
  */
-public final class NodeExecCounter extends TruffleTool {
+public final class NodeExecCounter extends InstrumentationTool {
 
     /**
      * Execution count for AST nodes of a particular type.
--- a/mx/mx_graal.py	Tue Feb 10 11:15:39 2015 +0100
+++ b/mx/mx_graal.py	Tue Feb 10 14:43:12 2015 +0100
@@ -2280,7 +2280,7 @@
     complt += '\t(args)\n'
     # TODO: improve matcher: if mx args are given, this doesn't work
     complt += '\t\tcase $line[1] in\n'
-    complt += '\t\t\t(vm)\n'
+    complt += '\t\t\t(vm | vmg | vmfg | unittest | jmh | dacapo | scaladacapo | specjvm2008 | specjbb2013 | specjbb2005)\n'
     complt += '\t\t\t\tnoglob \\\n'
     complt += '\t\t\t\t\t_arguments -s -S \\\n'
     complt += _appendOptions("graal", r"G\:")
@@ -2347,7 +2347,7 @@
 
     # gather graal options
     output = StringIO.StringIO()
-    vm(['-XX:-BootstrapGraal', '-G:+PrintFlags' if optionType == "graal" else '-XX:+PrintFlagsWithComments'],
+    vm(['-XX:-BootstrapGraal', '-XX:+UnlockDiagnosticVMOptions', '-G:+PrintFlags' if optionType == "graal" else '-XX:+PrintFlagsWithComments'],
        vm="graal",
        vmbuild="optimized",
        nonZeroIsFatal=False,
--- a/src/share/vm/runtime/arguments.cpp	Tue Feb 10 11:15:39 2015 +0100
+++ b/src/share/vm/runtime/arguments.cpp	Tue Feb 10 14:43:12 2015 +0100
@@ -3585,6 +3585,12 @@
     if (match_option(option, "-XX:-IgnoreUnrecognizedVMOptions", &tail)) {
       IgnoreUnrecognizedVMOptions = false;
     }
+    if (match_option(option, "-XX:+UnlockDiagnosticVMOptions", &tail)) {
+      UnlockDiagnosticVMOptions = true;
+    }
+    if (match_option(option, "-XX:-UnlockDiagnosticVMOptions", &tail)) {
+      UnlockDiagnosticVMOptions = false;
+    }
     if (match_option(option, "-XX:+PrintFlagsInitial", &tail)) {
       CommandLineFlags::printFlags(tty, false);
       vm_exit(0);
--- a/test/blacklist_sparc.txt	Tue Feb 10 11:15:39 2015 +0100
+++ b/test/blacklist_sparc.txt	Tue Feb 10 14:43:12 2015 +0100
@@ -1,4 +1,2 @@
 com.oracle.graal.replacements.test.StandardMethodSubstitutionsTest
 com.oracle.graal.hotspot.amd64.test.CompressedNullCheckTest
-com.oracle.graal.hotspot.test.HotSpotCryptoSubstitutionTest
-