changeset 19936:da997cdfb0d8

Merge
author Stefan Anzinger <stefan.anzinger@oracle.com>
date Wed, 18 Mar 2015 13:53:23 +0100
parents bcfca701c935 (current diff) b6af1acf00d6 (diff)
children 4fe66c16e942
files
diffstat 36 files changed, 981 insertions(+), 164 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Wed Mar 18 13:53:23 2015 +0100
@@ -1943,6 +1943,10 @@
         ADD.rmOp.emit(this, QWORD, dst, src);
     }
 
+    public final void addq(AMD64Address dst, Register src) {
+        ADD.mrOp.emit(this, QWORD, dst, src);
+    }
+
     public final void andq(Register dst, int imm32) {
         AND.getMIOpcode(QWORD, isByte(imm32)).emit(this, QWORD, dst, imm32);
     }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Wed Mar 18 13:53:23 2015 +0100
@@ -346,7 +346,7 @@
         AllocationContext allocContext = new AllocationContext(lirGen.getSpillMoveFactory());
         lirSuites.getAllocationStage().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, allocContext);
 
-        PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext();
+        PostAllocationOptimizationContext postAllocOptContext = new PostAllocationOptimizationContext(lirGen);
         lirSuites.getPostAllocationOptimizationStage().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, postAllocOptContext);
 
         return lirGenRes;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCounterOp.java	Wed Mar 18 13:53:23 2015 +0100
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015, 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.amd64;
+
+import static com.oracle.graal.amd64.AMD64.*;
+import static com.oracle.graal.api.code.ValueUtil.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.amd64.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.asm.*;
+
+@Opcode("BenchMarkCounter")
+public class AMD64HotSpotCounterOp extends HotSpotCounterOp {
+    public static final LIRInstructionClass<AMD64HotSpotCounterOp> TYPE = LIRInstructionClass.create(AMD64HotSpotCounterOp.class);
+
+    @Alive({OperandFlag.STACK, OperandFlag.UNINITIALIZED}) private StackSlotValue backupSlot;
+
+    public AMD64HotSpotCounterOp(String name, String group, Value increment, HotSpotRegistersProvider registers, HotSpotVMConfig config, StackSlotValue backupSlot) {
+        super(TYPE, name, group, increment, registers, config);
+        this.backupSlot = backupSlot;
+    }
+
+    public AMD64HotSpotCounterOp(String[] names, String[] groups, Value[] increments, HotSpotRegistersProvider registers, HotSpotVMConfig config, StackSlotValue backupSlot) {
+        super(TYPE, names, groups, increments, registers, config);
+        this.backupSlot = backupSlot;
+    }
+
+    @Override
+    public void emitCode(CompilationResultBuilder crb) {
+        AMD64MacroAssembler masm = (AMD64MacroAssembler) crb.asm;
+        TargetDescription target = crb.target;
+
+        Register scratch = rax;
+
+        // address for counters array
+        AMD64Address countersArrayAddr = new AMD64Address(thread, config.graalCountersThreadOffset);
+        Register countersArrayReg = scratch;
+
+        // backup scratch register
+        masm.movq((AMD64Address) crb.asAddress(backupSlot), scratch);
+
+        // load counters array
+        masm.movptr(countersArrayReg, countersArrayAddr);
+
+        forEachCounter((name, group, increment) -> emitIncrement(masm, target, countersArrayReg, name, group, increment));
+
+        // restore scratch register
+        masm.movq(scratch, (AMD64Address) crb.asAddress(backupSlot));
+    }
+
+    private void emitIncrement(AMD64MacroAssembler masm, TargetDescription target, Register countersArrayReg, String name, String group, Value increment) {
+        // address for counter value
+        AMD64Address counterAddr = new AMD64Address(countersArrayReg, getDisplacementForLongIndex(target, getIndex(name, group, increment)));
+        // increment counter (in memory)
+        if (isConstant(increment)) {
+            masm.incrementl(counterAddr, asInt(asConstant(increment)));
+        } else {
+            masm.addq(counterAddr, asRegister(increment));
+        }
+    }
+}
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Wed Mar 18 13:53:23 2015 +0100
@@ -39,9 +39,11 @@
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.spi.*;
+import com.oracle.graal.debug.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.HotSpotVMConfig.CompressEncoding;
 import com.oracle.graal.hotspot.amd64.AMD64HotSpotMove.HotSpotStoreConstantOp;
+import com.oracle.graal.hotspot.debug.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.stubs.*;
 import com.oracle.graal.lir.*;
@@ -54,6 +56,8 @@
 import com.oracle.graal.lir.amd64.AMD64Move.LoadOp;
 import com.oracle.graal.lir.amd64.AMD64Move.MoveFromRegOp;
 import com.oracle.graal.lir.amd64.AMD64Move.StoreOp;
+import com.oracle.graal.lir.asm.*;
+import com.oracle.graal.lir.framemap.*;
 import com.oracle.graal.lir.gen.*;
 
 /**
@@ -118,6 +122,41 @@
 
     SaveRbp saveRbp;
 
+    private static final class RescueSlotDummyOp extends LIRInstruction {
+        public static final LIRInstructionClass<RescueSlotDummyOp> TYPE = LIRInstructionClass.create(RescueSlotDummyOp.class);
+
+        @Alive({OperandFlag.STACK, OperandFlag.UNINITIALIZED}) private StackSlotValue slot;
+
+        public RescueSlotDummyOp(FrameMapBuilder frameMapBuilder, LIRKind kind) {
+            super(TYPE);
+            slot = frameMapBuilder.allocateSpillSlot(kind);
+        }
+
+        public StackSlotValue getSlot() {
+            return slot;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb) {
+        }
+    }
+
+    private RescueSlotDummyOp rescueSlotOp;
+
+    private StackSlotValue getOrInitRescueSlot() {
+        if (rescueSlotOp == null) {
+            // create dummy instruction to keep the rescue slot alive
+            rescueSlotOp = new RescueSlotDummyOp(getResult().getFrameMapBuilder(), getLIRKindTool().getWordKind());
+            // insert dummy instruction into the start block
+            LIR lir = getResult().getLIR();
+            List<LIRInstruction> instructions = lir.getLIRforBlock(lir.getControlFlowGraph().getStartBlock());
+            // Note: we do not insert at position 1 to avoid interference with the save rpb op
+            instructions.add(instructions.size() - 1, rescueSlotOp);
+            Debug.dump(lir, "created rescue dummy op");
+        }
+        return rescueSlotOp.getSlot();
+    }
+
     /**
      * List of epilogue operations that need to restore RBP.
      */
@@ -440,6 +479,10 @@
         for (AMD64HotSpotEpilogueOp op : epilogueOps) {
             op.savedRbp = savedRbp;
         }
+        if (BenchmarkCounters.enabled) {
+            // ensure that the rescue slot is available
+            getOrInitRescueSlot();
+        }
     }
 
     private static LIRKind toStackKind(LIRKind kind) {
@@ -626,4 +669,20 @@
             return super.canInlineConstant(c);
         }
     }
+
+    @Override
+    public LIRInstruction createBenchmarkCounter(String name, String group, Value increment) {
+        if (BenchmarkCounters.enabled) {
+            return new AMD64HotSpotCounterOp(name, group, increment, getProviders().getRegisters(), config, getOrInitRescueSlot());
+        }
+        return null;
+    }
+
+    @Override
+    public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
+        if (BenchmarkCounters.enabled) {
+            return new AMD64HotSpotCounterOp(names, groups, increments, getProviders().getRegisters(), config, getOrInitRescueSlot());
+        }
+        return null;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotCounterOp.java	Wed Mar 18 13:53:23 2015 +0100
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, 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.sparc;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.sparc.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.asm.*;
+
+@Opcode("BenchMarkCounter")
+public class SPARCHotSpotCounterOp extends HotSpotCounterOp {
+    public static final LIRInstructionClass<SPARCHotSpotCounterOp> TYPE = LIRInstructionClass.create(SPARCHotSpotCounterOp.class);
+
+    @Temp({OperandFlag.REG}) private AllocatableValue scratch0;
+    @Temp({OperandFlag.REG}) private AllocatableValue scratch1;
+
+    public SPARCHotSpotCounterOp(String name, String group, Value increment, HotSpotRegistersProvider registers, HotSpotVMConfig config, AllocatableValue scratch0, AllocatableValue scratch1) {
+        super(TYPE, name, group, increment, registers, config);
+        this.scratch0 = scratch0;
+        this.scratch1 = scratch1;
+    }
+
+    public SPARCHotSpotCounterOp(String[] names, String[] groups, Value[] increments, HotSpotRegistersProvider registers, HotSpotVMConfig config, AllocatableValue scratch0, AllocatableValue scratch1) {
+        super(TYPE, names, groups, increments, registers, config);
+        this.scratch0 = scratch0;
+        this.scratch1 = scratch1;
+    }
+
+    @Override
+    public void emitCode(CompilationResultBuilder crb) {
+        SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
+        TargetDescription target = crb.target;
+
+        // address for counters array
+        SPARCAddress countersArrayAddr = new SPARCAddress(thread, config.graalCountersThreadOffset);
+        Register countersArrayReg = asRegister(scratch0);
+
+        // load counters array
+        masm.ldx(countersArrayAddr, countersArrayReg);
+
+        forEachCounter((name, group, increment) -> emitIncrement(masm, target, countersArrayReg, name, group, increment));
+    }
+
+    private void emitIncrement(SPARCMacroAssembler masm, TargetDescription target, Register countersArrayReg, String name, String group, Value increment) {
+        // address for counter
+        SPARCAddress counterAddr = new SPARCAddress(countersArrayReg, getDisplacementForLongIndex(target, getIndex(name, group, increment)));
+        Register counterReg = asRegister(scratch1);
+        // load counter value
+        masm.ldx(counterAddr, counterReg);
+        // increment counter
+        if (isConstant(increment)) {
+            masm.add(counterReg, asInt(asConstant(increment)), counterReg);
+        } else {
+            masm.add(counterReg, asRegister(increment), counterReg);
+        }
+        // store counter value
+        masm.stx(counterReg, counterAddr);
+    }
+}
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerator.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerator.java	Wed Mar 18 13:53:23 2015 +0100
@@ -37,6 +37,7 @@
 import com.oracle.graal.compiler.sparc.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.HotSpotVMConfig.CompressEncoding;
+import com.oracle.graal.hotspot.debug.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.stubs.*;
 import com.oracle.graal.lir.*;
@@ -48,6 +49,7 @@
 import com.oracle.graal.lir.sparc.SPARCMove.NullCheckOp;
 import com.oracle.graal.lir.sparc.SPARCMove.StoreConstantOp;
 import com.oracle.graal.lir.sparc.SPARCMove.StoreOp;
+import com.oracle.graal.sparc.*;
 
 public class SPARCHotSpotLIRGenerator extends SPARCLIRGenerator implements HotSpotLIRGenerator {
 
@@ -362,4 +364,32 @@
         assert kind == Kind.Object || kind == Kind.Long : address + " - " + kind + " not an object!";
         append(new NullCheckOp(load(address), state));
     }
+
+    @Override
+    public LIRInstruction createBenchmarkCounter(String name, String group, Value increment) {
+        if (BenchmarkCounters.enabled) {
+            try (SPARCScratchRegister sc0 = SPARCScratchRegister.get()) {
+                RegisterValue scratch0 = sc0.getRegister().asValue(getLIRKindTool().getWordKind());
+                try (SPARCScratchRegister sc1 = SPARCScratchRegister.get()) {
+                    RegisterValue scratch1 = sc1.getRegister().asValue(getLIRKindTool().getWordKind());
+                    return new SPARCHotSpotCounterOp(name, group, increment, getProviders().getRegisters(), config, scratch0, scratch1);
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
+        if (BenchmarkCounters.enabled) {
+            try (SPARCScratchRegister sc0 = SPARCScratchRegister.get()) {
+                RegisterValue scratch0 = sc0.getRegister().asValue(getLIRKindTool().getWordKind());
+                try (SPARCScratchRegister sc1 = SPARCScratchRegister.get()) {
+                    RegisterValue scratch1 = sc1.getRegister().asValue(getLIRKindTool().getWordKind());
+                    return new SPARCHotSpotCounterOp(names, groups, increments, getProviders().getRegisters(), config, scratch0, scratch1);
+                }
+            }
+        }
+        return null;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotCounterOp.java	Wed Mar 18 13:53:23 2015 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2015, 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;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.hotspot.debug.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.lir.*;
+
+public abstract class HotSpotCounterOp extends LIRInstruction {
+    public static final LIRInstructionClass<HotSpotCounterOp> TYPE = LIRInstructionClass.create(HotSpotCounterOp.class);
+
+    private final String[] names;
+    private final String[] groups;
+    protected final Register thread;
+    protected final HotSpotVMConfig config;
+    @Alive({OperandFlag.CONST, OperandFlag.REG}) protected Value[] increments;
+
+    public HotSpotCounterOp(LIRInstructionClass<? extends HotSpotCounterOp> c, String name, String group, Value increment, HotSpotRegistersProvider registers, HotSpotVMConfig config) {
+        this(c, new String[]{name}, new String[]{group}, new Value[]{increment}, registers, config);
+    }
+
+    public HotSpotCounterOp(LIRInstructionClass<? extends HotSpotCounterOp> c, String[] names, String[] groups, Value[] increments, HotSpotRegistersProvider registers, HotSpotVMConfig config) {
+        super(c);
+
+        assert names.length == groups.length;
+        assert groups.length == increments.length;
+
+        this.names = names;
+        this.groups = groups;
+        this.increments = increments;
+        this.thread = registers.getThreadRegister();
+        this.config = config;
+    }
+
+    protected static int getDisplacementForLongIndex(TargetDescription target, long index) {
+        long finalDisp = index * target.getSizeInBytes(Kind.Long);
+        if (!NumUtil.isInt(finalDisp)) {
+            throw GraalInternalError.unimplemented("cannot deal with indices that big: " + index);
+        }
+        return (int) finalDisp;
+    }
+
+    protected interface CounterProcedure {
+        void apply(String name, String group, Value increment);
+    }
+
+    protected void forEachCounter(CounterProcedure proc) {
+        for (int i = 0; i < names.length; i++) {
+            proc.apply(names[i], groups[i], increments[i]);
+        }
+    }
+
+    protected int getIndex(String name, String group, Value increment) {
+        if (isConstant(increment)) {
+            // get index for the counter
+            return BenchmarkCounters.getIndexConstantIncrement(name, group, config, asLong(asConstant(increment)));
+        }
+        assert isRegister(increment) : "Unexpected Value: " + increment;
+        // get index for the counter
+        return BenchmarkCounters.getIndex(name, group, config);
+    }
+
+    private static long asLong(JavaConstant value) {
+        Kind kind = value.getKind();
+        switch (kind) {
+            case Byte:
+            case Short:
+            case Char:
+            case Int:
+                return value.asInt();
+            case Long:
+                return value.asLong();
+            default:
+                throw new IllegalArgumentException("not an integer kind: " + kind);
+        }
+    }
+
+    protected static int asInt(JavaConstant value) {
+        long l = asLong(value);
+        if (!NumUtil.isInt(l)) {
+            throw GraalInternalError.shouldNotReachHere("value does not fit into int: " + l);
+        }
+        return (int) l;
+    }
+
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Wed Mar 18 13:53:23 2015 +0100
@@ -27,26 +27,13 @@
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.*;
 
-import sun.misc.*;
-
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.bridge.*;
-import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.replacements.*;
-import com.oracle.graal.nodeinfo.*;
-import com.oracle.graal.nodes.HeapAccess.BarrierType;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.debug.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.options.*;
-import com.oracle.graal.replacements.nodes.*;
 
 import edu.umd.cs.findbugs.annotations.*;
 
@@ -98,6 +85,8 @@
                        "  dacapo = 'err, starting =====, PASSED in'%n" +
                        "  specjvm2008 = 'out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends:'", type = OptionType.Debug)
         private static final OptionValue<String> BenchmarkDynamicCounters = new OptionValue<>(null);
+        @Option(help = "Use grouping separators for number printing", type = OptionType.Debug)
+        private static final OptionValue<Boolean> DynamicCountersPrintGroupSeparator = new OptionValue<>(true);
         //@formatter:on
     }
 
@@ -105,59 +94,62 @@
 
     public static boolean enabled = false;
 
-    public static final ConcurrentHashMap<String, Integer> indexes = new ConcurrentHashMap<>();
-    public static final ArrayList<String> groups = new ArrayList<>();
+    private static class Counter {
+        public final int index;
+        public final String group;
+        public final AtomicLong staticCounters;
+
+        public Counter(int index, String group, AtomicLong staticCounters) {
+            this.index = index;
+            this.group = group;
+            this.staticCounters = staticCounters;
+        }
+    }
+
+    public static final ConcurrentHashMap<String, Counter> counterMap = new ConcurrentHashMap<>();
     public static long[] delta;
-    public static final ArrayList<AtomicLong> staticCounters = new ArrayList<>();
+
+    public static int getIndexConstantIncrement(String name, String group, HotSpotVMConfig config, long increment) {
+        Counter counter = getCounter(name, group, config);
+        counter.staticCounters.addAndGet(increment);
+        return counter.index;
+    }
+
+    public static int getIndex(String name, String group, HotSpotVMConfig config) {
+        Counter counter = getCounter(name, group, config);
+        return counter.index;
+    }
 
     @SuppressFBWarnings(value = "AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION", justification = "concurrent abstraction calls are in synchronized block")
-    private static int getIndex(DynamicCounterNode counter, StructuredGraph currentGraph) {
+    private static Counter getCounter(String name, String group, HotSpotVMConfig config) throws GraalInternalError {
         if (!enabled) {
-            throw new GraalInternalError("counter nodes shouldn't exist when counters are not enabled: " + counter.getGroup() + ", " + counter.getName());
+            throw new GraalInternalError("cannot access count index when counters are not enabled: " + group + ", " + name);
         }
-        String name;
-        String group = counter.getGroup();
-        if (counter.isWithContext()) {
-            name = counter.getName() + " @ ";
-            if (currentGraph.method() != null) {
-                StackTraceElement stackTraceElement = currentGraph.method().asStackTraceElement(0);
-                if (stackTraceElement != null) {
-                    name += " " + stackTraceElement.toString();
-                } else {
-                    name += currentGraph.method().format("%h.%n");
-                }
-            }
-            if (currentGraph.name != null) {
-                name += " (" + currentGraph.name + ")";
-            }
-            name += "#" + group;
-
-        } else {
-            name = counter.getName() + "#" + group;
-        }
-        Integer index = indexes.get(name);
-        if (index == null) {
+        String nameGroup = name + "#" + group;
+        Counter counter = counterMap.get(nameGroup);
+        if (counter == null) {
             synchronized (BenchmarkCounters.class) {
-                index = indexes.get(name);
-                if (index == null) {
-                    index = indexes.size();
-                    indexes.put(name, index);
-                    groups.add(group);
-                    staticCounters.add(new AtomicLong());
+                counter = counterMap.get(nameGroup);
+                if (counter == null) {
+                    counter = new Counter(counterMap.size(), group, new AtomicLong());
+                    counterMap.put(nameGroup, counter);
                 }
             }
         }
-        assert groups.get(index).equals(group) : "mismatching groups: " + groups.get(index) + " vs. " + group;
-        if (counter.getIncrement().isConstant()) {
-            staticCounters.get(index).addAndGet(counter.getIncrement().asJavaConstant().asLong());
+        assert counter.group.equals(group) : "mismatching groups: " + counter.group + " vs. " + group;
+        int countersSize = config.graalCountersSize;
+        if (counter.index >= countersSize) {
+            throw new GraalInternalError("too many counters, reduce number of counters or increase -XX:GraalCounterSize=... (current value: " + countersSize + ")");
         }
-        return index;
+        return counter;
     }
 
     private static synchronized void dump(PrintStream out, double seconds, long[] counters, int maxRows) {
-        if (!groups.isEmpty()) {
-            out.println("====== dynamic counters (" + staticCounters.size() + " in total) ======");
-            for (String group : new TreeSet<>(groups)) {
+        if (!counterMap.isEmpty()) {
+            out.println("====== dynamic counters (" + counterMap.size() + " in total) ======");
+            TreeSet<String> set = new TreeSet<>();
+            counterMap.forEach((nameGroup, counter) -> set.add(counter.group));
+            for (String group : set) {
                 if (group != null) {
                     if (DUMP_STATIC) {
                         dumpCounters(out, seconds, counters, true, group, maxRows);
@@ -181,9 +173,9 @@
         // collect the numbers
         long[] array;
         if (staticCounter) {
-            array = new long[indexes.size()];
-            for (int i = 0; i < array.length; i++) {
-                array[i] = staticCounters.get(i).get();
+            array = new long[counterMap.size()];
+            for (Counter counter : counterMap.values()) {
+                array[counter.index] = counter.staticCounters.get();
             }
         } else {
             array = counters.clone();
@@ -193,9 +185,10 @@
         }
         // sort the counters by putting them into a sorted map
         long sum = 0;
-        for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
-            int index = entry.getValue();
-            if (groups.get(index).equals(group)) {
+        for (Map.Entry<String, Counter> entry : counterMap.entrySet()) {
+            Counter counter = entry.getValue();
+            int index = counter.index;
+            if (counter.group.equals(group)) {
                 sum += array[index];
                 sorted.put(array[index] * array.length + index, entry.getKey().substring(0, entry.getKey().length() - group.length() - 1));
             }
@@ -216,28 +209,29 @@
                 cnt--;
             }
 
+            String numFmt = Options.DynamicCountersPrintGroupSeparator.getValue() ? "%,19d" : "%19d";
             if (staticCounter) {
                 out.println("=========== " + group + " (static counters):");
                 for (Map.Entry<Long, String> entry : sorted.entrySet()) {
                     long counter = entry.getKey() / array.length;
-                    out.format(Locale.US, "%,19d %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
+                    out.format(Locale.US, numFmt + " %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
                 }
-                out.format(Locale.US, "%,19d total\n", sum);
+                out.format(Locale.US, numFmt + " total\n", sum);
             } else {
                 if (group.startsWith("~")) {
                     out.println("=========== " + group + " (dynamic counters), time = " + seconds + " s:");
                     for (Map.Entry<Long, String> entry : sorted.entrySet()) {
                         long counter = entry.getKey() / array.length;
-                        out.format(Locale.US, "%,19d/s %3d%%  %s\n", (long) (counter / seconds), percentage(counter, sum), entry.getValue());
+                        out.format(Locale.US, numFmt + "/s %3d%%  %s\n", (long) (counter / seconds), percentage(counter, sum), entry.getValue());
                     }
-                    out.format(Locale.US, "%,19d/s total\n", (long) (sum / seconds));
+                    out.format(Locale.US, numFmt + "/s total\n", (long) (sum / seconds));
                 } else {
                     out.println("=========== " + group + " (dynamic counters):");
                     for (Map.Entry<Long, String> entry : sorted.entrySet()) {
                         long counter = entry.getKey() / array.length;
-                        out.format(Locale.US, "%,19d %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
+                        out.format(Locale.US, numFmt + " %3d%%  %s\n", counter, percentage(counter, sum), entry.getValue());
                     }
-                    out.format(Locale.US, "%,19d total\n", sum);
+                    out.format(Locale.US, numFmt + " total\n", sum);
                 }
             }
         }
@@ -383,51 +377,4 @@
             dump(TTY.cachedOut, (System.nanoTime() - compilerStartTime) / 1000000000d, compilerToVM.collectCounters(), 100);
         }
     }
-
-    private static final LocationIdentity COUNTER_ARRAY_LOCATION = NamedLocationIdentity.mutable("COUNTER_ARRAY_LOCATION");
-    private static final LocationIdentity COUNTER_LOCATION = NamedLocationIdentity.mutable("COUNTER_LOCATION");
-
-    @NodeInfo(nameTemplate = "CounterIndex")
-    private static final class CounterIndexNode extends FloatingNode implements LIRLowerable {
-
-        public static final NodeClass<CounterIndexNode> TYPE = NodeClass.create(CounterIndexNode.class);
-        protected final Object counter;
-        protected final int countersSize;
-
-        protected CounterIndexNode(Stamp stamp, DynamicCounterNode counter, int countersSize) {
-            super(TYPE, stamp);
-            this.countersSize = countersSize;
-            this.counter = counter;
-        }
-
-        @Override
-        public void generate(NodeLIRBuilderTool generator) {
-            int index = BenchmarkCounters.getIndex((DynamicCounterNode) counter, graph());
-            if (index >= countersSize) {
-                throw new GraalInternalError("too many counters, reduce number of counters or increase -XX:GraalCounterSize=... (current value: " + countersSize + ")");
-            }
-
-            generator.setResult(this, JavaConstant.forIntegerKind(getKind(), index));
-        }
-    }
-
-    public static void lower(DynamicCounterNode counter, HotSpotRegistersProvider registers, HotSpotVMConfig config, Kind wordKind) {
-        StructuredGraph graph = counter.graph();
-
-        ReadRegisterNode thread = graph.add(new ReadRegisterNode(registers.getThreadRegister(), wordKind, true, false));
-
-        CounterIndexNode index = graph.unique(new CounterIndexNode(StampFactory.forKind(wordKind), counter, config.graalCountersSize));
-        ConstantLocationNode arrayLocation = graph.unique(new ConstantLocationNode(COUNTER_ARRAY_LOCATION, config.graalCountersThreadOffset));
-        ReadNode readArray = graph.add(new ReadNode(thread, arrayLocation, StampFactory.forKind(wordKind), BarrierType.NONE));
-        IndexedLocationNode location = graph.unique(new IndexedLocationNode(COUNTER_LOCATION, 0, index, Unsafe.ARRAY_LONG_INDEX_SCALE));
-        ReadNode read = graph.add(new ReadNode(readArray, location, StampFactory.forKind(Kind.Long), BarrierType.NONE));
-        AddNode add = graph.unique(new AddNode(read, counter.getIncrement()));
-        WriteNode write = graph.add(new WriteNode(readArray, add, location, BarrierType.NONE));
-
-        graph.addBeforeFixed(counter, thread);
-        graph.addBeforeFixed(counter, readArray);
-        graph.addBeforeFixed(counter, read);
-        graph.addBeforeFixed(counter, write);
-        graph.removeFixed(counter);
-    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Wed Mar 18 13:53:23 2015 +0100
@@ -36,7 +36,6 @@
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.debug.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.nodes.type.*;
 import com.oracle.graal.hotspot.replacements.*;
@@ -105,8 +104,6 @@
             lowerStoreHubNode((StoreHubNode) n, graph);
         } else if (n instanceof OSRStartNode) {
             lowerOSRStartNode((OSRStartNode) n);
-        } else if (n instanceof DynamicCounterNode) {
-            lowerDynamicCounterNode((DynamicCounterNode) n);
         } else if (n instanceof BytecodeExceptionNode) {
             lowerBytecodeExceptionNode((BytecodeExceptionNode) n);
         } else if (n instanceof CheckCastDynamicNode) {
@@ -369,13 +366,6 @@
         }
     }
 
-    private void lowerDynamicCounterNode(DynamicCounterNode n) {
-        StructuredGraph graph = n.graph();
-        if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
-            BenchmarkCounters.lower(n, registers, runtime.getConfig(), runtime.getTarget().wordKind);
-        }
-    }
-
     static final class Exceptions {
         protected static final ArrayIndexOutOfBoundsException cachedArrayIndexOutOfBoundsException;
         protected static final NullPointerException cachedNullPointerException;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaFieldImpl.java	Wed Mar 18 13:53:23 2015 +0100
@@ -149,9 +149,9 @@
 
     @Override
     public JavaType getType() {
-        if (!(type instanceof ResolvedJavaType)) {
+        if (type instanceof HotSpotUnresolvedJavaType) {
             // Don't allow unresolved types to hang around forever
-            ResolvedJavaType resolved = type.resolve(holder);
+            ResolvedJavaType resolved = ((HotSpotUnresolvedJavaType) type).reresolve(holder);
             if (resolved != null) {
                 type = resolved;
             }
@@ -204,7 +204,7 @@
         }
         try {
             return holder.mirror().getDeclaredField(name);
-        } catch (NoSuchFieldException e) {
+        } catch (NoSuchFieldException | NoClassDefFoundError e) {
             return null;
         }
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java	Wed Mar 18 13:53:23 2015 +0100
@@ -88,4 +88,18 @@
     public ResolvedJavaType resolve(ResolvedJavaType accessingClass) {
         return (ResolvedJavaType) runtime.lookupType(getName(), (HotSpotResolvedObjectType) accessingClass, true);
     }
+
+    /**
+     * Try to find a loaded version of this class.
+     *
+     * @param accessingClass
+     * @return the resolved class or null.
+     */
+    ResolvedJavaType reresolve(HotSpotResolvedObjectType accessingClass) {
+        JavaType type = runtime.lookupType(getName(), accessingClass, false);
+        if (type instanceof ResolvedJavaType) {
+            return (ResolvedJavaType) type;
+        }
+        return null;
+    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Wed Mar 18 13:53:23 2015 +0100
@@ -157,7 +157,8 @@
     public static native Object newInstance(@ConstantNodeParameter ForeignCallDescriptor descriptor, KlassPointer hub);
 
     @Snippet
-    public static Object allocateInstanceDynamic(Class<?> type, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter String typeContext) {
+    public static Object allocateInstanceDynamic(Class<?> type, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister,
+                    @SuppressWarnings("unused") @ConstantParameter String typeContext) {
         KlassPointer hub = ClassGetHubNode.readClass(type);
         if (probability(FAST_PATH_PROBABILITY, !hub.isNull())) {
             if (probability(FAST_PATH_PROBABILITY, isInstanceKlassFullyInitialized(hub))) {
@@ -170,7 +171,11 @@
                  */
                 if (probability(FAST_PATH_PROBABILITY, (layoutHelper & 1) == 0)) {
                     Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-                    return allocateInstance(layoutHelper, hub, prototypeMarkWord, fillContents, threadRegister, false, typeContext);
+                    /*
+                     * FIXME(je,ds): we should actually pass typeContext instead of "" but late
+                     * binding of parameters is not yet supported by the GraphBuilderPlugin system.
+                     */
+                    return allocateInstance(layoutHelper, hub, prototypeMarkWord, fillContents, threadRegister, false, "");
                 }
             }
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/Stub.java	Wed Mar 18 13:53:23 2015 +0100
@@ -40,6 +40,8 @@
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.lir.phases.*;
+import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
+import com.oracle.graal.lir.profiling.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.schedule.*;
@@ -180,7 +182,7 @@
                     Suites suites = new Suites(new PhaseSuite<>(), defaultSuites.getMidTier(), defaultSuites.getLowTier());
                     SchedulePhase schedule = emitFrontEnd(providers, target, graph, null, providers.getSuites().getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, getProfilingInfo(graph),
                                     null, suites);
-                    LIRSuites lirSuites = providers.getSuites().getDefaultLIRSuites();
+                    LIRSuites lirSuites = createLIRSuites();
                     emitBackEnd(graph, Stub.this, incomingCc, getInstalledCodeOwner(), backend, target, compResult, CompilationResultBuilderFactory.Default, schedule, getRegisterConfig(), lirSuites);
                 } catch (Throwable e) {
                     throw Debug.handle(e);
@@ -210,6 +212,15 @@
         return code;
     }
 
+    private LIRSuites createLIRSuites() {
+        LIRSuites lirSuites = new LIRSuites(providers.getSuites().getDefaultLIRSuites());
+        ListIterator<LIRPhase<PostAllocationOptimizationContext>> moveProfiling = lirSuites.getPostAllocationOptimizationStage().findPhase(MoveProfiling.class);
+        if (moveProfiling != null) {
+            moveProfiling.remove();
+        }
+        return lirSuites;
+    }
+
     /**
      * Gets the compilation result for this stub, compiling it first if necessary, and installing it
      * in code.
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ControlFlowOptimizer.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ControlFlowOptimizer.java	Wed Mar 18 13:53:23 2015 +0100
@@ -41,7 +41,8 @@
      * Performs control flow optimizations on the given LIR graph.
      */
     @Override
-    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder) {
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory) {
         LIR lir = lirGenRes.getLIR();
         new Optimizer<B>(lir).deleteEmptyBlocks(codeEmittingOrder);
     }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/EdgeMoveOptimizer.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/EdgeMoveOptimizer.java	Wed Mar 18 13:53:23 2015 +0100
@@ -51,7 +51,8 @@
 public final class EdgeMoveOptimizer extends PostAllocationOptimizationPhase {
 
     @Override
-    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder) {
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory) {
         LIR ir = lirGenRes.getLIR();
         Optimizer optimizer = new Optimizer(ir);
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/NullCheckOptimizer.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/NullCheckOptimizer.java	Wed Mar 18 13:53:23 2015 +0100
@@ -34,7 +34,8 @@
 public final class NullCheckOptimizer extends PostAllocationOptimizationPhase {
 
     @Override
-    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder) {
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory) {
         LIR ir = lirGenRes.getLIR();
         List<? extends AbstractBlockBase<?>> blocks = ir.codeEmittingOrder();
         NullCheckOptimizer.foldNullChecks(ir, blocks, target.implicitNullCheckLimit);
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java	Wed Mar 18 13:53:23 2015 +0100
@@ -44,7 +44,8 @@
 public final class RedundantMoveElimination extends PostAllocationOptimizationPhase {
 
     @Override
-    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder) {
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory) {
         Optimization redundantMoveElimination = new Optimization();
         redundantMoveElimination.doOptimize(lirGenRes.getLIR(), lirGenRes.getFrameMap());
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/BenchmarkCounterFactory.java	Wed Mar 18 13:53:23 2015 +0100
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 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.lir.gen;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.lir.*;
+
+public interface BenchmarkCounterFactory {
+    LIRInstruction createBenchmarkCounter(String name, String group, Value increment);
+
+    LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments);
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerator.java	Wed Mar 18 13:53:23 2015 +0100
@@ -391,4 +391,12 @@
     public void emitBlackhole(Value operand) {
         append(new StandardOp.BlackholeOp(operand));
     }
+
+    public LIRInstruction createBenchmarkCounter(String name, String group, Value increment) {
+        throw GraalInternalError.unimplemented();
+    }
+
+    public LIRInstruction createMultiBenchmarkCounter(String[] names, String[] groups, Value[] increments) {
+        throw GraalInternalError.unimplemented();
+    }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGeneratorTool.java	Wed Mar 18 13:53:23 2015 +0100
@@ -30,7 +30,7 @@
 import com.oracle.graal.compiler.common.spi.*;
 import com.oracle.graal.lir.*;
 
-public interface LIRGeneratorTool extends ArithmeticLIRGenerator {
+public interface LIRGeneratorTool extends ArithmeticLIRGenerator, BenchmarkCounterFactory {
 
     public interface SpillMoveFactory {
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/LIRPhaseSuite.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/LIRPhaseSuite.java	Wed Mar 18 13:53:23 2015 +0100
@@ -28,7 +28,7 @@
 import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.lir.gen.*;
 
-public abstract class LIRPhaseSuite<C> extends LIRPhase<C> {
+public class LIRPhaseSuite<C> extends LIRPhase<C> {
     private final List<LIRPhase<C>> phases;
 
     public LIRPhaseSuite() {
@@ -75,4 +75,9 @@
         }
     }
 
+    public LIRPhaseSuite<C> copy() {
+        LIRPhaseSuite<C> suite = new LIRPhaseSuite<>();
+        suite.phases.addAll(phases);
+        return suite;
+    }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/LIRSuites.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/LIRSuites.java	Wed Mar 18 13:53:23 2015 +0100
@@ -41,6 +41,10 @@
         this.postAllocStage = postAllocStage;
     }
 
+    public LIRSuites(LIRSuites other) {
+        this(other.getPreAllocationOptimizationStage().copy(), other.getAllocationStage().copy(), other.getPostAllocationOptimizationStage().copy());
+    }
+
     /**
      * {@link PreAllocationOptimizationPhase}s are executed between {@link LIR} generation and
      * register allocation.
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationPhase.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationPhase.java	Wed Mar 18 13:53:23 2015 +0100
@@ -31,14 +31,20 @@
 public abstract class PostAllocationOptimizationPhase extends LIRPhase<PostAllocationOptimizationPhase.PostAllocationOptimizationContext> {
 
     public static final class PostAllocationOptimizationContext {
+        private final BenchmarkCounterFactory counterFactory;
+
+        public PostAllocationOptimizationContext(BenchmarkCounterFactory counterFactory) {
+            this.counterFactory = counterFactory;
+        }
     }
 
     @Override
     protected final <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
                     PostAllocationOptimizationContext context) {
-        run(target, lirGenRes, codeEmittingOrder, linearScanOrder);
+        run(target, lirGenRes, codeEmittingOrder, linearScanOrder, context.counterFactory);
     }
 
-    protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder);
+    protected abstract <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory);
 
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PostAllocationOptimizationStage.java	Wed Mar 18 13:53:23 2015 +0100
@@ -25,7 +25,8 @@
 import static com.oracle.graal.lir.phases.LIRPhase.Options.*;
 
 import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase.*;
+import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
+import com.oracle.graal.lir.profiling.*;
 import com.oracle.graal.options.*;
 
 public class PostAllocationOptimizationStage extends LIRPhaseSuite<PostAllocationOptimizationContext> {
@@ -39,6 +40,8 @@
         public static final NestedBooleanOptionValue LIROptRedundantMoveElimination = new NestedBooleanOptionValue(LIROptimization, true);
         @Option(help = "", type = OptionType.Debug)
         public static final NestedBooleanOptionValue LIROptNullCheckOptimizer = new NestedBooleanOptionValue(LIROptimization, true);
+        @Option(help = "", type = OptionType.Debug)
+        public static final OptionValue<Boolean> LIROptMoveProfiling = new OptionValue<>(false);
         // @formatter:on
     }
 
@@ -55,5 +58,8 @@
         if (Options.LIROptNullCheckOptimizer.getValue()) {
             appendPhase(new NullCheckOptimizer());
         }
+        if (Options.LIROptMoveProfiling.getValue()) {
+            appendPhase(new MoveProfiling());
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/profiling/MoveProfiling.java	Wed Mar 18 13:53:23 2015 +0100
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2015, 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.lir.profiling;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.StandardOp.BlockEndOp;
+import com.oracle.graal.lir.StandardOp.LabelOp;
+import com.oracle.graal.lir.StandardOp.MoveOp;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.phases.*;
+
+public class MoveProfiling extends PostAllocationOptimizationPhase {
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory) {
+        new Analyzer(lirGenRes.getLIR(), counterFactory).run();
+    }
+
+    private static enum MoveType {
+        REG2REG("Reg", "Reg"),
+        STACK2REG("Reg", "Stack"),
+        CONST2REG("Reg", "Const"),
+        REG2STACK("Stack", "Reg"),
+        CONST2STACK("Stack", "Const");
+
+        private final String name;
+
+        MoveType(String dst, String src) {
+            this.name = String.format("%5s <- %s", dst, src);
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        public static MoveType get(MoveOp move) {
+            AllocatableValue dst = move.getResult();
+            Value src = move.getInput();
+            if (isRegister(dst)) {
+                if (isRegister(src)) {
+                    return REG2REG;
+                }
+                if (isStackSlot(src)) {
+                    return STACK2REG;
+                }
+                if (isConstant(src)) {
+                    return CONST2REG;
+                }
+            } else if (isStackSlot(dst)) {
+                if (isRegister(src)) {
+                    return REG2STACK;
+                }
+                if (isConstant(src)) {
+                    return CONST2STACK;
+                }
+            }
+            throw GraalInternalError.shouldNotReachHere(String.format("Unrecognized Move: %s dst=%s, src=%s", move, dst, src));
+        }
+    }
+
+    private static class Analyzer {
+        private final LIR lir;
+        private final BenchmarkCounterFactory counterFactory;
+        private final LIRInsertionBuffer buffer;
+        private final int[] cnt;
+
+        public Analyzer(LIR lir, BenchmarkCounterFactory counterFactory) {
+            this.lir = lir;
+            this.counterFactory = counterFactory;
+            this.buffer = new LIRInsertionBuffer();
+            cnt = new int[MoveType.values().length];
+        }
+
+        public void run() {
+            for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
+                doBlock(block);
+            }
+        }
+
+        public void doBlock(AbstractBlockBase<?> block) {
+            List<LIRInstruction> instructions = lir.getLIRforBlock(block);
+            assert instructions.size() >= 2 : "Malformed block: " + block + ", " + instructions;
+            assert instructions.get(instructions.size() - 1) instanceof BlockEndOp : "Not a BlockEndOp: " + instructions.get(instructions.size() - 1);
+            assert !(instructions.get(instructions.size() - 2) instanceof BlockEndOp) : "Is a BlockEndOp: " + instructions.get(instructions.size() - 2);
+            assert instructions.get(0) instanceof LabelOp : "Not a LabelOp: " + instructions.get(0);
+            assert !(instructions.get(1) instanceof LabelOp) : "Is a LabelOp: " + instructions.get(1);
+
+            // reset counters
+            Arrays.fill(cnt, 0);
+            // analysis phase
+            for (LIRInstruction inst : instructions) {
+                if (inst instanceof MoveOp) {
+                    cnt[MoveType.get((MoveOp) inst).ordinal()]++;
+                }
+            }
+
+            // counter insertion phase
+            List<String> names = new ArrayList<>();
+            List<Value> increments = new ArrayList<>();
+            for (MoveType type : MoveType.values()) {
+                int i = cnt[type.ordinal()];
+                if (i > 0) {
+                    names.add(type.toString());
+                    increments.add(JavaConstant.forInt(i));
+                }
+            }
+            String[] groups = new String[names.size()];
+            Arrays.fill(groups, "Move Operations");
+
+            LIRInstruction inst = counterFactory.createMultiBenchmarkCounter(names.toArray(new String[0]), groups, increments.toArray(new Value[0]));
+            assert inst != null;
+            buffer.init(instructions);
+            buffer.append(1, inst);
+            buffer.finish();
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/CountedLoopInfo.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/CountedLoopInfo.java	Wed Mar 18 13:53:23 2015 +0100
@@ -60,6 +60,7 @@
             if (iv.direction() == Direction.Up) {
                 range = add(graph, range, ConstantNode.forIntegerStamp(stamp, 1, graph));
             } else {
+                assert iv.direction() == Direction.Down;
                 range = sub(graph, range, ConstantNode.forIntegerStamp(stamp, 1, graph));
             }
         }
@@ -76,6 +77,7 @@
     }
 
     public long constantMaxTripCount() {
+        assert iv.direction() != null;
         long off = oneOff ? iv.direction() == Direction.Up ? 1 : -1 : 0;
         long max = (((ConstantNode) end).asJavaConstant().asLong() + off - iv.constantInit()) / iv.constantStride();
         return Math.max(0, max);
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Wed Mar 18 13:53:23 2015 +0100
@@ -218,11 +218,12 @@
                         if (initStamp.upperBound() > limitStamp.lowerBound()) {
                             return false;
                         }
-                    } else {
-                        assert iv.direction() == Direction.Down;
+                    } else if (iv.direction() == Direction.Down) {
                         if (initStamp.lowerBound() < limitStamp.upperBound()) {
                             return false;
                         }
+                    } else {
+                        return false;
                     }
                     oneOff = true;
                     break;
@@ -265,9 +266,12 @@
             if (loop().getExits().contains(b)) {
                 exits.add((LoopExitNode) b.getBeginNode());
             } else {
-                assert loop().getBlocks().contains(b);
                 blocks.add(b.getBeginNode());
-                work.addAll(b.getDominated());
+                for (Block d : b.getDominated()) {
+                    if (loop.getBlocks().contains(d)) {
+                        work.add(d);
+                    }
+                }
             }
         }
         return LoopFragment.computeNodes(branch.graph(), blocks, exits);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Wed Mar 18 13:53:23 2015 +0100
@@ -22,8 +22,11 @@
  */
 package com.oracle.graal.nodes.debug;
 
+import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
@@ -37,7 +40,7 @@
  * value of withContext, the name of the root method is added to the counter's name.
  */
 @NodeInfo
-public class DynamicCounterNode extends FixedWithNextNode implements Lowerable {
+public class DynamicCounterNode extends FixedWithNextNode implements LIRLowerable {
 
     public static final NodeClass<DynamicCounterNode> TYPE = NodeClass.create(DynamicCounterNode.class);
     @Input ValueNode increment;
@@ -74,11 +77,6 @@
         return withContext;
     }
 
-    @Override
-    public void lower(LoweringTool tool) {
-        tool.getLowerer().lower(this, tool);
-    }
-
     public static void addCounterBefore(String group, String name, long increment, boolean withContext, FixedNode position) {
         StructuredGraph graph = position.graph();
         graph.addBeforeFixed(position, position.graph().add(new DynamicCounterNode(name, group, ConstantNode.forLong(increment, position.graph()), withContext)));
@@ -87,4 +85,32 @@
     @NodeIntrinsic
     public static native void counter(@ConstantNodeParameter String name, @ConstantNodeParameter String group, long increment, @ConstantNodeParameter boolean addContext);
 
+    public void generate(NodeLIRBuilderTool generator) {
+        LIRGeneratorTool lirGen = generator.getLIRGeneratorTool();
+        String nameWithContext;
+        if (isWithContext()) {
+            nameWithContext = getName() + " @ ";
+            if (graph().method() != null) {
+                StackTraceElement stackTraceElement = graph().method().asStackTraceElement(0);
+                if (stackTraceElement != null) {
+                    nameWithContext += " " + stackTraceElement.toString();
+                } else {
+                    nameWithContext += graph().method().format("%h.%n");
+                }
+            }
+            if (graph().name != null) {
+                nameWithContext += " (" + graph().name + ")";
+            }
+
+        } else {
+            nameWithContext = getName();
+        }
+        LIRInstruction counterOp = lirGen.createBenchmarkCounter(nameWithContext, getGroup(), generator.operand(increment));
+        if (counterOp != null) {
+            lirGen.append(counterOp);
+        } else {
+            throw GraalInternalError.unimplemented("Benchmark counters not enabled or not implemented by the back end.");
+        }
+    }
+
 }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/MemoryScheduleVerification.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/MemoryScheduleVerification.java	Wed Mar 18 13:53:23 2015 +0100
@@ -31,6 +31,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 
@@ -78,6 +79,8 @@
                 }
 
                 addFloatingReadUsages(currentState, n);
+            } else if (n instanceof MemoryProxy) {
+                addFloatingReadUsages(currentState, n);
             } else if (n instanceof FloatingReadNode) {
                 FloatingReadNode floatingReadNode = (FloatingReadNode) n;
                 if (floatingReadNode.getLastLocationAccess() != null && floatingReadNode.getLocationIdentity().isMutable()) {
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Wed Mar 18 13:53:23 2015 +0100
@@ -253,7 +253,7 @@
                 } else {
                     // silently ignored
                 }
-            } catch (InterruptedException e) {
+            } catch (InterruptedException | CancellationException e) {
                 // silently ignored
             }
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Mar 18 13:53:23 2015 +0100
@@ -319,11 +319,18 @@
         plugins.setLoadFieldPlugin(new InterceptLoadFieldPlugin());
         plugins.setParameterPlugin(new InterceptReceiverPlugin(callTarget));
         callTarget.setInlining(new TruffleInlining(callTarget, new DefaultInliningPolicy()));
-        plugins.setInlineInvokePlugin(new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements()));
+        InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements());
+        if (PrintTruffleExpansionHistogram.getValue()) {
+            inlinePlugin = new HistogramInlineInvokePlugin(graph, inlinePlugin);
+        }
+        plugins.setInlineInvokePlugin(inlinePlugin);
         plugins.setLoopExplosionPlugin(new PELoopExplosionPlugin());
         InvocationPlugins invocationPlugins = newConfig.getPlugins().getInvocationPlugins();
         new GraphBuilderPhase.Instance(providers.getMetaAccess(), providers.getStampProvider(), this.snippetReflection, providers.getConstantReflection(), newConfig,
                         TruffleCompilerImpl.Optimizations, null).apply(graph);
+        if (PrintTruffleExpansionHistogram.getValue()) {
+            ((HistogramInlineInvokePlugin) inlinePlugin).print(callTarget, System.out);
+        }
         Debug.dump(graph, "After FastPE");
 
         // Perform deoptimize to guard conversion.
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Tue Mar 17 18:57:47 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Wed Mar 18 13:53:23 2015 +0100
@@ -156,6 +156,9 @@
     @Option(help = "Print source secions for printed expansion trees", type = OptionType.Debug)
     public static final OptionValue<Boolean> TraceTruffleExpansionSource = new OptionValue<>(false);
 
+    @Option(help = "Prints a histogram of all expanded Java methods.", type = OptionType.Debug)
+    public static final OptionValue<Boolean> PrintTruffleExpansionHistogram = new OptionValue<>(false);
+
     @Option(help = "Print detailed information for the Truffle compilation cache", type = OptionType.Debug)
     public static final OptionValue<Boolean> TraceTruffleCacheDetails = new OptionValue<>(false);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/debug/HistogramInlineInvokePlugin.java	Wed Mar 18 13:53:23 2015 +0100
@@ -0,0 +1,179 @@
+/*
+ * 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.debug;
+
+import java.io.*;
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.truffle.*;
+
+public class HistogramInlineInvokePlugin implements InlineInvokePlugin {
+
+    private final Map<ResolvedJavaMethod, MethodStatistics> histogram = new HashMap<>();
+    private final StructuredGraph graph;
+    private final InlineInvokePlugin delegate;
+
+    private HistogramInlineInvokePlugin.MethodStatistic currentStatistic;
+
+    public HistogramInlineInvokePlugin(StructuredGraph graph, InlineInvokePlugin delegate) {
+        this.graph = graph;
+        this.delegate = delegate;
+    }
+
+    public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
+        InlineInfo inlineInfo = delegate.getInlineInfo(b, method, args, returnType);
+        if (inlineInfo != null) {
+            currentStatistic = new MethodStatistic(currentStatistic, inlineInfo.methodToInline, graph.getNodeCount(), graph.getNodes(MethodCallTargetNode.TYPE).count());
+        }
+        return inlineInfo;
+    }
+
+    public void postInline(ResolvedJavaMethod inlinedTargetMethod) {
+        delegate.postInline(inlinedTargetMethod);
+
+        if (currentStatistic != null) {
+            currentStatistic.applyNodeCountAfter(graph.getNodeCount());
+            currentStatistic.applyCallsAfter(graph.getNodes(MethodCallTargetNode.TYPE).count());
+            accept(currentStatistic);
+            currentStatistic = currentStatistic.getParent();
+        }
+    }
+
+    private void accept(MethodStatistic current) {
+        ResolvedJavaMethod method = current.getMethod();
+        HistogramInlineInvokePlugin.MethodStatistics statistics = histogram.get(method);
+        if (statistics == null) {
+            statistics = new MethodStatistics(method);
+            histogram.put(method, statistics);
+        }
+        statistics.accept(current);
+    }
+
+    public void print(OptimizedCallTarget target, PrintStream out) {
+        out.printf("Truffle expansion histogram for %s", target);
+        out.println("  Invocations = Number of expanded invocations");
+        out.println("  Nodes = Number of Graal nodes created for this method during partial evaluation.");
+        out.println("  Calls = Number of not expanded calls created for this method during partial evaluation.");
+        out.printf(" %-11s |Nodes %5s %5s %5s %8s |Calls %5s %5s %5s %8s | Method Name%n", "Invocations", "Sum", "Min", "Max", "Avg", "Sum", "Min", "Max", "Avg");
+        histogram.values().stream().sorted().forEach(statistics -> statistics.print(out));
+    }
+
+    private static class MethodStatistics implements Comparable<MethodStatistics> {
+
+        private final ResolvedJavaMethod method;
+
+        private int count;
+        private final IntSummaryStatistics shallowCount = new IntSummaryStatistics();
+        private final IntSummaryStatistics callCount = new IntSummaryStatistics();
+
+        public MethodStatistics(ResolvedJavaMethod method) {
+            this.method = method;
+        }
+
+        public void print(PrintStream out) {
+            out.printf(" %11d |        %5d %5d %5d %8.2f |      %5d %5d %5d %8.2f | %s%n", //
+                            count, shallowCount.getSum(), shallowCount.getMin(), shallowCount.getMax(), //
+                            shallowCount.getAverage(), callCount.getSum(), callCount.getMin(), callCount.getMax(), //
+                            callCount.getAverage(), method.format("%h.%n(%p)"));
+        }
+
+        public int compareTo(MethodStatistics o) {
+            int result = (int) (o.shallowCount.getSum() - shallowCount.getSum());
+            if (result == 0) {
+                return o.count - count;
+            }
+            return result;
+        }
+
+        public void accept(MethodStatistic statistic) {
+            if (!statistic.method.equals(method)) {
+                throw new IllegalArgumentException("invalid statistic");
+            }
+            count++;
+            callCount.accept(statistic.getShallowCallCount());
+            shallowCount.accept(statistic.getShallowNodeCount());
+        }
+    }
+
+    private static class MethodStatistic {
+
+        private final MethodStatistic parent;
+        private final List<MethodStatistic> children = new ArrayList<>();
+
+        private final ResolvedJavaMethod method;
+        private int deepNodeCount;
+        private int callCount;
+
+        public MethodStatistic(MethodStatistic parent, ResolvedJavaMethod method, int nodeCountBefore, int callsBefore) {
+            this.parent = parent;
+            this.method = method;
+            this.callCount = callsBefore;
+            this.deepNodeCount = nodeCountBefore;
+            if (parent != null) {
+                this.parent.getChildren().add(this);
+            }
+        }
+
+        public ResolvedJavaMethod getMethod() {
+            return method;
+        }
+
+        public List<MethodStatistic> getChildren() {
+            return children;
+        }
+
+        public int getShallowNodeCount() {
+            int shallowCount = deepNodeCount;
+            for (MethodStatistic child : children) {
+                shallowCount -= child.deepNodeCount;
+            }
+            return shallowCount;
+        }
+
+        public int getShallowCallCount() {
+            int shallowCount = callCount;
+            for (MethodStatistic child : children) {
+                shallowCount -= child.callCount;
+            }
+            return shallowCount;
+        }
+
+        public void applyNodeCountAfter(int nodeCountAfter) {
+            deepNodeCount = nodeCountAfter - this.deepNodeCount;
+        }
+
+        public void applyCallsAfter(int callsAfter) {
+            callCount = callsAfter - this.callCount;
+        }
+
+        public MethodStatistic getParent() {
+            return parent;
+        }
+
+    }
+
+}
--- a/mxtool/mx.py	Tue Mar 17 18:57:47 2015 +0100
+++ b/mxtool/mx.py	Wed Mar 18 13:53:23 2015 +0100
@@ -2823,7 +2823,7 @@
         else:
             # Using just SC_ARG_MAX without extra downwards adjustment
             # results in "[Errno 7] Argument list too long" on MacOS.
-            commandLinePrefixAllowance -= 20000
+            commandLinePrefixAllowance = 20000
             syslimit = os.sysconf('SC_ARG_MAX')
             if syslimit == -1:
                 syslimit = 262144 # we could use sys.maxint but we prefer a more robust smaller value
--- a/src/share/vm/graal/graalRuntime.cpp	Tue Mar 17 18:57:47 2015 +0100
+++ b/src/share/vm/graal/graalRuntime.cpp	Wed Mar 18 13:53:23 2015 +0100
@@ -626,6 +626,7 @@
 
 // private static GraalRuntime Graal.initializeRuntime()
 JVM_ENTRY(jobject, JVM_GetGraalRuntime(JNIEnv *env, jclass c))
+  GraalRuntime::initialize_HotSpotGraalRuntime();
   return GraalRuntime::get_HotSpotGraalRuntime_jobject();
 JVM_END
 
@@ -688,21 +689,43 @@
   }
 }
 
-Handle GraalRuntime::get_HotSpotGraalRuntime() {
+Handle GraalRuntime::callInitializer(const char* className, const char* methodName, const char* returnType) {
+  guarantee(!_HotSpotGraalRuntime_initialized, "cannot reinitialize HotSpotGraalRuntime");
+  Thread* THREAD = Thread::current();
+  check_generated_sources_sha1(CHECK_ABORT_(Handle()));
+
+  TempNewSymbol name = SymbolTable::new_symbol(className, CHECK_ABORT_(Handle()));
+  KlassHandle klass = load_required_class(name);
+  TempNewSymbol runtime = SymbolTable::new_symbol(methodName, CHECK_ABORT_(Handle()));
+  TempNewSymbol sig = SymbolTable::new_symbol(returnType, CHECK_ABORT_(Handle()));
+  JavaValue result(T_OBJECT);
+  JavaCalls::call_static(&result, klass, runtime, sig, CHECK_ABORT_(Handle()));
+  return Handle((oop)result.get_jobject());
+}
+
+void GraalRuntime::initialize_HotSpotGraalRuntime() {
   if (JNIHandles::resolve(_HotSpotGraalRuntime_instance) == NULL) {
-    guarantee(!_HotSpotGraalRuntime_initialized, "cannot reinitialize HotSpotGraalRuntime");
+#ifdef ASSERT
+    // This should only be called in the context of the Graal class being initialized
     Thread* THREAD = Thread::current();
-    check_generated_sources_sha1(CHECK_ABORT_(Handle()));
-    TempNewSymbol name = SymbolTable::new_symbol("com/oracle/graal/hotspot/HotSpotGraalRuntime", CHECK_ABORT_(Handle()));
-    KlassHandle klass = load_required_class(name);
-    TempNewSymbol runtime = SymbolTable::new_symbol("runtime", CHECK_ABORT_(Handle()));
-    TempNewSymbol sig = SymbolTable::new_symbol("()Lcom/oracle/graal/hotspot/HotSpotGraalRuntime;", CHECK_ABORT_(Handle()));
-    JavaValue result(T_OBJECT);
-    JavaCalls::call_static(&result, klass, runtime, sig, CHECK_ABORT_(Handle()));
-    _HotSpotGraalRuntime_instance = JNIHandles::make_global((oop) result.get_jobject());
+    TempNewSymbol name = SymbolTable::new_symbol("com/oracle/graal/api/runtime/Graal", CHECK_ABORT);
+    instanceKlassHandle klass = InstanceKlass::cast(load_required_class(name));
+    assert(klass->is_being_initialized() && klass->is_reentrant_initialization(THREAD),
+           "HotSpotGraalRuntime initialization should only be triggered through Graal initialization");
+#endif
+
+    Handle result = callInitializer("com/oracle/graal/hotspot/HotSpotGraalRuntime", "runtime",
+                                    "()Lcom/oracle/graal/hotspot/HotSpotGraalRuntime;");
     _HotSpotGraalRuntime_initialized = true;
+    _HotSpotGraalRuntime_instance = JNIHandles::make_global(result());
   }
-  return Handle(JNIHandles::resolve_non_null(_HotSpotGraalRuntime_instance));
+}
+
+void GraalRuntime::initialize_Graal() {
+  if (JNIHandles::resolve(_HotSpotGraalRuntime_instance) == NULL) {
+    callInitializer("com/oracle/graal/api/runtime/Graal",     "getRuntime",      "()Lcom/oracle/graal/api/runtime/GraalRuntime;");
+  }
+  assert(_HotSpotGraalRuntime_initialized == true, "what?");
 }
 
 // private static void CompilerToVMImpl.init()
--- a/src/share/vm/graal/graalRuntime.hpp	Tue Mar 17 18:57:47 2015 +0100
+++ b/src/share/vm/graal/graalRuntime.hpp	Wed Mar 18 13:53:23 2015 +0100
@@ -138,13 +138,29 @@
   /**
    * Gets the singleton HotSpotGraalRuntime instance, initializing it if necessary
    */
-  static Handle get_HotSpotGraalRuntime();
+  static Handle get_HotSpotGraalRuntime() {
+    initialize_Graal();
+    return Handle(JNIHandles::resolve_non_null(_HotSpotGraalRuntime_instance));
+  }
 
   static jobject get_HotSpotGraalRuntime_jobject() {
-    get_HotSpotGraalRuntime();
+    initialize_Graal();
+    assert(_HotSpotGraalRuntime_initialized, "must be");
     return _HotSpotGraalRuntime_instance;
   }
 
+  static Handle callInitializer(const char* className, const char* methodName, const char* returnType);
+
+  /**
+   * Trigger initialization of HotSpotGraalRuntime through Graal.runtime()
+   */
+  static void initialize_Graal();
+
+  /**
+   * Explicitly initialize HotSpotGraalRuntime itself
+   */
+  static void initialize_HotSpotGraalRuntime();
+
   static void shutdown();
 
   static bool shutdown_called() {