changeset 20003:422e60a2f4b9

Implement dynamic instruction counters on assembly level
author Stefan Anzinger <stefan.anzinger@oracle.com>
date Mon, 23 Mar 2015 15:58:36 +0100
parents 59c2d62927f7
children 220c494e5088
files graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCAssembler.java graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCInstructionCounter.java graal/com.oracle.graal.asm/src/com/oracle/graal/asm/Assembler.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/Backend.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCounterOp.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerationResult.java graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotCounterOp.java graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerationResult.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotCounterOp.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotInstructionProfiling.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerationResult.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerationResultBase.java graal/com.oracle.graal.lir/src/com/oracle/graal/lir/profiling/MoveProfiling.java
diffstat 18 files changed, 484 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCAssembler.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCAssembler.java	Mon Mar 23 15:58:36 2015 +0100
@@ -1741,4 +1741,20 @@
     public void casxa(Register rs1, Register rs2, Register rd, Asi asi) {
         ld(Casxa, new SPARCAddress(rs1, rs2), rd, asi);
     }
+
+    @Override
+    public InstructionCounter getInstructionCounter() {
+        return new SPARCInstructionCounter(this);
+    }
+
+    public void patchAddImmediate(int position, int simm13) {
+        int inst = getInt(position);
+        assert SPARCAssembler.isSimm13(simm13) : simm13;
+        assert (inst >>> 30) == 0b10 : String.format("0x%x", inst);
+        assert ((inst >>> 18) & 0b11_1111) == 0 : String.format("0x%x", inst);
+        assert (inst & (1 << 13)) != 0 : String.format("0x%x", inst);
+        inst = inst & (~((1 << 13) - 1));
+        inst |= simm13 & ((1 << 12) - 1);
+        emitInt(inst, position);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.asm.sparc/src/com/oracle/graal/asm/sparc/SPARCInstructionCounter.java	Mon Mar 23 15:58:36 2015 +0100
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009, 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.asm.sparc;
+
+import java.util.*;
+
+import com.oracle.graal.asm.Assembler.InstructionCounter;
+
+public class SPARCInstructionCounter implements InstructionCounter {
+    // Use a treemap to keep the order in the output
+    private static final TreeMap<String, SPARCInstructionMatch> INSTRUCTION_MATCHER = new TreeMap<>();
+    static {
+        // @formatter:off
+        INSTRUCTION_MATCHER.put("nop", new SPARCInstructionMatch(0xFFFF_FFFF, 0x0100_0000));
+        INSTRUCTION_MATCHER.put("st", new OP3LowBitsMatcher(0b11, 0x4, 0x5, 0x6, 0x7, 0xe, 0xf));
+        INSTRUCTION_MATCHER.put("ld", new OP3LowBitsMatcher(0b11, 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd));
+        INSTRUCTION_MATCHER.put("all", new SPARCInstructionMatch(0x0, 0x0));
+        // @formatter:on
+    }
+    private final SPARCAssembler asm;
+
+    public SPARCInstructionCounter(SPARCAssembler asm) {
+        super();
+        this.asm = asm;
+    }
+
+    @Override
+    public int[] countInstructions(String[] instructionTypes, int beginPc, int endPc) {
+        SPARCInstructionMatch[] matchers = new SPARCInstructionMatch[instructionTypes.length];
+        for (int i = 0; i < instructionTypes.length; i++) {
+            String typeName = instructionTypes[i];
+            matchers[i] = INSTRUCTION_MATCHER.get(typeName);
+            if (matchers[i] == null) {
+                throw new IllegalArgumentException(String.format("Unknown instruction class %s, supported types are: %s", typeName, INSTRUCTION_MATCHER.keySet()));
+            }
+        }
+        return countBetween(matchers, beginPc, endPc);
+    }
+
+    private int[] countBetween(SPARCInstructionMatch[] matchers, int startPc, int endPc) {
+        int[] counts = new int[matchers.length];
+        for (int p = startPc; p < endPc; p += 4) {
+            int instr = asm.getInt(p);
+            for (int i = 0; i < matchers.length; i++) {
+                SPARCInstructionMatch matcher = matchers[i];
+                if (matcher.matches(instr)) {
+                    counts[i]++;
+                }
+            }
+        }
+        return counts;
+    }
+
+    @Override
+    public String[] getSupportedInstructionTypes() {
+        return INSTRUCTION_MATCHER.keySet().toArray(new String[0]);
+    }
+
+    /**
+     * Tests the lower 3 bits of the op3 field.
+     */
+    private static class OP3LowBitsMatcher extends SPARCInstructionMatch {
+        private final int[] op3b03;
+        private final int op;
+
+        public OP3LowBitsMatcher(int op, int... op3b03) {
+            super(0, 0);
+            this.op = op;
+            this.op3b03 = op3b03;
+        }
+
+        @Override
+        public boolean matches(int instruction) {
+            if (instruction >>> 30 != op) {
+                return false;
+            }
+            int op3lo = (instruction >> 19) & ((1 << 4) - 1);
+            for (int op3Part : op3b03) {
+                if (op3Part == op3lo) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private static class SPARCInstructionMatch {
+        private final int mask;
+        private final int[] patterns;
+
+        public SPARCInstructionMatch(int mask, int... patterns) {
+            super();
+            this.mask = mask;
+            this.patterns = patterns;
+        }
+
+        public boolean matches(int instruction) {
+            for (int pattern : patterns) {
+                if ((instruction & mask) == pattern) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
--- a/graal/com.oracle.graal.asm/src/com/oracle/graal/asm/Assembler.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.asm/src/com/oracle/graal/asm/Assembler.java	Mon Mar 23 15:58:36 2015 +0100
@@ -213,6 +213,10 @@
         return hint;
     }
 
+    public InstructionCounter getInstructionCounter() {
+        throw new UnsupportedOperationException("Instruction counter is not implemented for " + this);
+    }
+
     public static class LabelHint {
         private Label label;
         private int forPosition;
@@ -242,4 +246,14 @@
             return capturedTarget >= 0;
         }
     }
+
+    /**
+     * Instruction counter class which gives the user of the assembler to count different kinds of
+     * instructions in the generated assembler code.
+     */
+    public interface InstructionCounter {
+        String[] getSupportedInstructionTypes();
+
+        int[] countInstructions(String[] instructionTypes, int beginPc, int endPc);
+    }
 }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Mon Mar 23 15:58:36 2015 +0100
@@ -46,6 +46,7 @@
 import com.oracle.graal.lir.phases.*;
 import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
 import com.oracle.graal.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext;
+import com.oracle.graal.lir.profiling.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
@@ -320,7 +321,14 @@
                 throw Debug.handle(e);
             }
             FrameMapBuilder frameMapBuilder = backend.newFrameMapBuilder(registerConfig);
-            LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(lir, frameMapBuilder, graph.method(), stub);
+            String compilationUnitName;
+            ResolvedJavaMethod method = graph.method();
+            if (method == null) {
+                compilationUnitName = "<unknown>";
+            } else {
+                compilationUnitName = method.format("%H.%n(%p)");
+            }
+            LIRGenerationResult lirGenRes = backend.newLIRGenerationResult(compilationUnitName, lir, frameMapBuilder, graph.method(), stub);
             LIRGeneratorTool lirGen = backend.newLIRGenerator(cc, lirGenRes);
             NodeLIRBuilderTool nodeLirGen = backend.newNodeLIRBuilder(graph, lirGen);
 
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/Backend.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/Backend.java	Mon Mar 23 15:58:36 2015 +0100
@@ -78,7 +78,7 @@
 
     public abstract LIRGeneratorTool newLIRGenerator(CallingConvention cc, LIRGenerationResult lirGenRes);
 
-    public abstract LIRGenerationResult newLIRGenerationResult(LIR lir, FrameMapBuilder frameMapBuilder, ResolvedJavaMethod method, Object stub);
+    public abstract LIRGenerationResult newLIRGenerationResult(String compilationUnitName, LIR lir, FrameMapBuilder frameMapBuilder, ResolvedJavaMethod method, Object stub);
 
     public abstract NodeLIRBuilderTool newNodeLIRBuilder(StructuredGraph graph, LIRGeneratorTool lirGen);
 
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Mon Mar 23 15:58:36 2015 +0100
@@ -78,8 +78,8 @@
     }
 
     @Override
-    public LIRGenerationResult newLIRGenerationResult(LIR lir, FrameMapBuilder frameMapBuilder, ResolvedJavaMethod method, Object stub) {
-        return new AMD64HotSpotLIRGenerationResult(lir, frameMapBuilder, stub);
+    public LIRGenerationResult newLIRGenerationResult(String compilationUnitName, LIR lir, FrameMapBuilder frameMapBuilder, ResolvedJavaMethod method, Object stub) {
+        return new AMD64HotSpotLIRGenerationResult(compilationUnitName, lir, frameMapBuilder, stub);
     }
 
     @Override
@@ -254,6 +254,9 @@
 
         // Emit the suffix
         emitCodeSuffix(installedCodeOwner, crb, asm, frameMap);
+
+        // Profile assembler instructions
+        profileInstructions(lir, crb);
     }
 
     /**
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCounterOp.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotCounterOp.java	Mon Mar 23 15:58:36 2015 +0100
@@ -65,16 +65,16 @@
 
         // load counters array
         masm.movptr(countersArrayReg, countersArrayAddr);
-
-        forEachCounter((name, group, increment) -> emitIncrement(masm, target, countersArrayReg, name, group, increment));
+        CounterProcedure emitProcedure = (counterIndex, increment, displacement) -> emitIncrement(masm, countersArrayReg, increment, displacement);
+        forEachCounter(emitProcedure, target);
 
         // 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) {
+    private static void emitIncrement(AMD64MacroAssembler masm, Register countersArrayReg, Value increment, int displacement) {
         // address for counter value
-        AMD64Address counterAddr = new AMD64Address(countersArrayReg, getDisplacementForLongIndex(target, getIndex(name, group, increment)));
+        AMD64Address counterAddr = new AMD64Address(countersArrayReg, displacement);
         // increment counter (in memory)
         if (isConstant(increment)) {
             masm.incrementl(counterAddr, asInt(asConstant(increment)));
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerationResult.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerationResult.java	Mon Mar 23 15:58:36 2015 +0100
@@ -48,8 +48,8 @@
      */
     private Map<LIRFrameState, SaveRegistersOp> calleeSaveInfo = CollectionsFactory.newMap();
 
-    public AMD64HotSpotLIRGenerationResult(LIR lir, FrameMapBuilder frameMapBuilder, Object stub) {
-        super(lir, frameMapBuilder);
+    public AMD64HotSpotLIRGenerationResult(String compilationUnitName, LIR lir, FrameMapBuilder frameMapBuilder, Object stub) {
+        super(compilationUnitName, lir, frameMapBuilder);
         this.stub = stub;
     }
 
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java	Mon Mar 23 15:58:36 2015 +0100
@@ -80,8 +80,8 @@
     }
 
     @Override
-    public LIRGenerationResult newLIRGenerationResult(LIR lir, FrameMapBuilder frameMapBuilder, ResolvedJavaMethod method, Object stub) {
-        return new SPARCHotSpotLIRGenerationResult(lir, frameMapBuilder, stub);
+    public LIRGenerationResult newLIRGenerationResult(String compilationUnitName, LIR lir, FrameMapBuilder frameMapBuilder, ResolvedJavaMethod method, Object stub) {
+        return new SPARCHotSpotLIRGenerationResult(compilationUnitName, lir, frameMapBuilder, stub);
     }
 
     @Override
@@ -247,6 +247,8 @@
             crb.emit(lir);
         } while (i++ < 1);
 
+        profileInstructions(lir, crb);
+
         HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext;
         HotSpotForeignCallsProvider foreignCalls = getProviders().getForeignCalls();
         if (!frameContext.isStub) {
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotCounterOp.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotCounterOp.java	Mon Mar 23 15:58:36 2015 +0100
@@ -23,11 +23,14 @@
 package com.oracle.graal.hotspot.sparc;
 
 import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.asm.sparc.SPARCAssembler.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
 import com.oracle.graal.asm.sparc.*;
-import com.oracle.graal.asm.sparc.SPARCMacroAssembler.*;
+import com.oracle.graal.asm.sparc.SPARCMacroAssembler.ScratchRegister;
+import com.oracle.graal.asm.sparc.SPARCMacroAssembler.Setx;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.lir.*;
@@ -37,12 +40,16 @@
 public class SPARCHotSpotCounterOp extends HotSpotCounterOp {
     public static final LIRInstructionClass<SPARCHotSpotCounterOp> TYPE = LIRInstructionClass.create(SPARCHotSpotCounterOp.class);
 
+    private int[] counterPatchOffsets;
+
     public SPARCHotSpotCounterOp(String name, String group, Value increment, HotSpotRegistersProvider registers, HotSpotVMConfig config) {
         super(TYPE, name, group, increment, registers, config);
+        this.counterPatchOffsets = new int[1];
     }
 
     public SPARCHotSpotCounterOp(String[] names, String[] groups, Value[] increments, HotSpotRegistersProvider registers, HotSpotVMConfig config) {
         super(TYPE, names, groups, increments, registers, config);
+        this.counterPatchOffsets = new int[names.length];
     }
 
     @Override
@@ -57,19 +64,17 @@
 
             // load counters array
             masm.ldx(countersArrayAddr, countersArrayReg);
-
-            forEachCounter((name, group, increment) -> emitIncrement(masm, target, countersArrayReg, name, group, increment));
+            IncrementEmitter emitter = new IncrementEmitter(countersArrayReg, masm);
+            forEachCounter(emitter, target);
         }
     }
 
-    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)));
-
+    private void emitIncrement(int counterIndex, SPARCMacroAssembler masm, SPARCAddress counterAddr, Value increment) {
         try (ScratchRegister scratch = masm.getScratchRegister()) {
             Register counterReg = scratch.getRegister();
             // load counter value
             masm.ldx(counterAddr, counterReg);
+            counterPatchOffsets[counterIndex] = masm.position();
             // increment counter
             if (isConstant(increment)) {
                 masm.add(counterReg, asInt(asConstant(increment)), counterReg);
@@ -80,4 +85,53 @@
             masm.stx(counterReg, counterAddr);
         }
     }
+
+    /**
+     * Patches the increment value in the instruction emitted by the
+     * {@link #emitIncrement(int, SPARCMacroAssembler, SPARCAddress, Value)} method. This method is
+     * used if patching is needed after assembly.
+     *
+     * @param asm
+     * @param increment
+     */
+    @Override
+    public void patchCounterIncrement(Assembler asm, int[] increment) {
+        for (int i = 0; i < increment.length; i++) {
+            int inst = counterPatchOffsets[i];
+            ((SPARCAssembler) asm).patchAddImmediate(inst, increment[i]);
+        }
+    }
+
+    public int[] getCounterPatchOffsets() {
+        return counterPatchOffsets;
+    }
+
+    private class IncrementEmitter implements CounterProcedure {
+        private int lastDisplacement = 0;
+        private final Register countersArrayReg;
+        private final SPARCMacroAssembler masm;
+
+        public IncrementEmitter(Register countersArrayReg, SPARCMacroAssembler masm) {
+            super();
+            this.countersArrayReg = countersArrayReg;
+            this.masm = masm;
+        }
+
+        public void apply(int counterIndex, Value increment, int displacement) {
+            SPARCAddress counterAddr;
+            int relativeDisplacement = displacement - lastDisplacement;
+            if (isSimm13(relativeDisplacement)) { // Displacement fits into ld instruction
+                counterAddr = new SPARCAddress(countersArrayReg, relativeDisplacement);
+            } else {
+                try (ScratchRegister scratch = masm.getScratchRegister()) {
+                    Register tempOffsetRegister = scratch.getRegister();
+                    new Setx(relativeDisplacement, tempOffsetRegister, false).emit(masm);
+                    masm.add(countersArrayReg, tempOffsetRegister, countersArrayReg);
+                }
+                lastDisplacement = displacement;
+                counterAddr = new SPARCAddress(countersArrayReg, 0);
+            }
+            emitIncrement(counterIndex, masm, counterAddr, increment);
+        }
+    }
 }
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerationResult.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerationResult.java	Mon Mar 23 15:58:36 2015 +0100
@@ -47,8 +47,8 @@
      */
     private Map<LIRFrameState, SaveRegistersOp> calleeSaveInfo = new HashMap<>();
 
-    public SPARCHotSpotLIRGenerationResult(LIR lir, FrameMapBuilder frameMapBuilder, Object stub) {
-        super(lir, frameMapBuilder);
+    public SPARCHotSpotLIRGenerationResult(String compilationUnitName, LIR lir, FrameMapBuilder frameMapBuilder, Object stub) {
+        super(compilationUnitName, lir, frameMapBuilder);
         this.stub = stub;
     }
 
@@ -67,5 +67,4 @@
     Map<LIRFrameState, SaveRegistersOp> getCalleeSaveInfo() {
         return calleeSaveInfo;
     }
-
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotBackend.java	Mon Mar 23 15:58:36 2015 +0100
@@ -40,6 +40,7 @@
 import com.oracle.graal.lir.LIRInstruction.OperandMode;
 import com.oracle.graal.lir.StandardOp.LabelOp;
 import com.oracle.graal.lir.StandardOp.SaveRegistersOp;
+import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.lir.framemap.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.options.*;
@@ -55,6 +56,9 @@
         // @formatter:off
         @Option(help = "Use Graal stubs instead of HotSpot stubs where possible")
         public static final OptionValue<Boolean> PreferGraalStubs = new OptionValue<>(false);
+        @Option(help = "Enables instruction profiling on assembler level. Valid values are a comma separated list of supported instructions." +
+                        " Compare with subclasses of Assembler.InstructionCounter.", type = OptionType.Debug)
+        public static final OptionValue<String> ASMInstructionProfiling = new OptionValue<>(null);
         // @formatter:on
     }
 
@@ -235,4 +239,10 @@
     public DisassemblerProvider getDisassembler() {
         return getProviders().getDisassembler();
     }
+
+    protected void profileInstructions(LIR lir, CompilationResultBuilder crb) {
+        if (HotSpotBackend.Options.ASMInstructionProfiling.getValue() != null) {
+            HotSpotInstructionProfiling.countInstructions(lir, crb.asm);
+        }
+    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotCounterOp.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotCounterOp.java	Mon Mar 23 15:58:36 2015 +0100
@@ -24,6 +24,8 @@
 
 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.asm.*;
@@ -67,12 +69,42 @@
     }
 
     protected interface CounterProcedure {
-        void apply(String name, String group, Value increment);
+        /**
+         * Lambda interface for iterating over counters declared in this op.
+         *
+         * @param counterIndex Index in this CounterOp object.
+         * @param increment Value for increment
+         * @param displacement Displacement in bytes in the counter array
+         */
+        void apply(int counterIndex, Value increment, int displacement);
     }
 
-    protected void forEachCounter(CounterProcedure proc) {
-        for (int i = 0; i < names.length; i++) {
-            proc.apply(names[i], groups[i], increments[i]);
+    /**
+     * Calls the {@link CounterProcedure} for each counter in ascending order of their displacement
+     * in the counter array.
+     *
+     * @param proc The procedure to be called
+     * @param target Target architecture (used to calculate the array displacements)
+     */
+    protected void forEachCounter(CounterProcedure proc, TargetDescription target) {
+        if (names.length == 1) { // fast path
+            int arrayIndex = getIndex(names[0], groups[0], increments[0]);
+            int displacement = getDisplacementForLongIndex(target, arrayIndex);
+            proc.apply(0, increments[0], displacement);
+        } else { // Slow path with sort by displacements ascending
+            int[] displacements = new int[names.length];
+            HashMap<Integer, Integer> offsetMap = new HashMap<>(names.length);
+            for (int i = 0; i < names.length; i++) {
+                int arrayIndex = getIndex(names[i], groups[i], increments[i]);
+                displacements[i] = getDisplacementForLongIndex(target, arrayIndex);
+                offsetMap.put(displacements[i], i);
+            }
+            Arrays.sort(displacements);
+            // Now apply in order
+            for (int offset : displacements) {
+                int idx = offsetMap.get(offset);
+                proc.apply(idx, increments[idx], displacements[idx]);
+            }
         }
     }
 
@@ -86,6 +118,17 @@
         return BenchmarkCounters.getIndex(name, group, config);
     }
 
+    /**
+     * Patches the increment value in the instruction emitted by this instruction. Use only, if
+     * patching is needed after assembly.
+     *
+     * @param asm
+     * @param increment
+     */
+    public void patchCounterIncrement(Assembler asm, int[] increment) {
+        throw GraalInternalError.unimplemented();
+    }
+
     private static long asLong(JavaConstant value) {
         Kind kind = value.getKind();
         switch (kind) {
@@ -109,4 +152,11 @@
         return (int) l;
     }
 
+    public String[] getNames() {
+        return names;
+    }
+
+    public String[] getGroups() {
+        return groups;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotInstructionProfiling.java	Mon Mar 23 15:58:36 2015 +0100
@@ -0,0 +1,151 @@
+/*
+ * 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;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
+import com.oracle.graal.asm.Assembler.*;
+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.asm.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.phases.*;
+
+public class HotSpotInstructionProfiling extends PostAllocationOptimizationPhase {
+    public static final String COUNTER_GROUP = "INSTRUCTION_COUNTER";
+    private final String[] instructionsToProfile;
+
+    public HotSpotInstructionProfiling(String instructionsToProfile) {
+        this.instructionsToProfile = instructionsToProfile.split(",");
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder,
+                    BenchmarkCounterFactory counterFactory) {
+        new Analyzer(lirGenRes.getCompilationUnitName(), lirGenRes.getLIR(), counterFactory).run();
+    }
+
+    private class Analyzer {
+        private final LIR lir;
+        private final BenchmarkCounterFactory counterFactory;
+        private final LIRInsertionBuffer buffer;
+        private final String compilationUnitName;
+
+        public Analyzer(String compilationUnitName, LIR lir, BenchmarkCounterFactory counterFactory) {
+            this.lir = lir;
+            this.compilationUnitName = compilationUnitName;
+            this.counterFactory = counterFactory;
+            this.buffer = new LIRInsertionBuffer();
+        }
+
+        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);
+            String[] names = new String[instructionsToProfile.length];
+            String[] groups = new String[instructionsToProfile.length];
+            Value[] increments = new Value[instructionsToProfile.length];
+            for (int i = 0; i < instructionsToProfile.length; i++) {
+                names[i] = compilationUnitName;
+                groups[i] = COUNTER_GROUP + " " + instructionsToProfile[i];
+                increments[i] = JavaConstant.INT_0;
+            }
+            HotSpotCounterOp op = (HotSpotCounterOp) counterFactory.createMultiBenchmarkCounter(names, groups, increments);
+            LIRInstruction inst = new InstructionCounterOp(op, instructionsToProfile);
+            assert inst != null;
+            buffer.init(instructions);
+            buffer.append(1, inst);
+            buffer.finish();
+        }
+    }
+
+    /**
+     * After assembly the {@link HotSpotBackend#profileInstructions(LIR, CompilationResultBuilder)}
+     * calls this method for patching the instruction counts into the coutner increment code.
+     */
+    public static void countInstructions(LIR lir, Assembler asm) {
+        InstructionCounterOp lastOp = null;
+        InstructionCounter counter = asm.getInstructionCounter();
+        for (AbstractBlockBase<?> block : lir.codeEmittingOrder()) {
+            for (LIRInstruction inst : lir.getLIRforBlock(block)) {
+                if (inst instanceof InstructionCounterOp) {
+                    InstructionCounterOp currentOp = (InstructionCounterOp) inst;
+
+                    if (lastOp != null) {
+                        int beginPc = lastOp.countOffsetEnd;
+                        int endPc = currentOp.countOffsetBegin;
+                        int[] instructionCounts = counter.countInstructions(lastOp.instructionsToProfile, beginPc, endPc);
+                        lastOp.delegate.patchCounterIncrement(asm, instructionCounts);
+                    }
+                    lastOp = ((InstructionCounterOp) inst);
+                }
+            }
+        }
+        if (lastOp != null) {
+            assert lastOp.countOffsetBegin < asm.position();
+            int beginPc = lastOp.countOffsetBegin;
+            int endPc = asm.position();
+            int[] instructionCounts = counter.countInstructions(lastOp.instructionsToProfile, beginPc, endPc);
+            lastOp.delegate.patchCounterIncrement(asm, instructionCounts);
+        }
+    }
+
+    public static class InstructionCounterOp extends LIRInstruction {
+        public static final LIRInstructionClass<InstructionCounterOp> TYPE = LIRInstructionClass.create(InstructionCounterOp.class);
+        private final HotSpotCounterOp delegate;
+        private final String[] instructionsToProfile;
+        private int countOffsetBegin;
+        private int countOffsetEnd;
+
+        public InstructionCounterOp(HotSpotCounterOp delegate, String[] instructionsToProfile) {
+            super(TYPE);
+            this.delegate = delegate;
+            this.instructionsToProfile = instructionsToProfile;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb) {
+            countOffsetBegin = crb.asm.position();
+            this.delegate.emitCode(crb);
+            countOffsetEnd = crb.asm.position();
+        }
+
+        public String[] getInstructionsToProfile() {
+            return instructionsToProfile;
+        }
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Mon Mar 23 15:58:36 2015 +0100
@@ -132,7 +132,11 @@
     }
 
     public LIRSuites createLIRSuites() {
-        return Suites.createDefaultLIRSuites();
+        LIRSuites suites = Suites.createDefaultLIRSuites();
+        String profileInstructions = HotSpotBackend.Options.ASMInstructionProfiling.getValue();
+        if (profileInstructions != null) {
+            suites.getPostAllocationOptimizationStage().appendPhase(new HotSpotInstructionProfiling(profileInstructions));
+        }
+        return suites;
     }
-
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerationResult.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerationResult.java	Mon Mar 23 15:58:36 2015 +0100
@@ -56,4 +56,6 @@
     boolean hasForeignCall();
 
     void setForeignCall(boolean b);
+
+    String getCompilationUnitName();
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerationResultBase.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/LIRGenerationResultBase.java	Mon Mar 23 15:58:36 2015 +0100
@@ -34,10 +34,15 @@
      * Records whether the code being generated makes at least one foreign call.
      */
     private boolean hasForeignCall;
+    /**
+     * Human readable name of this compilation unit.
+     */
+    private final String compilationUnitName;
 
-    public LIRGenerationResultBase(LIR lir, FrameMapBuilder frameMapBuilder) {
+    public LIRGenerationResultBase(String compilationUnitName, LIR lir, FrameMapBuilder frameMapBuilder) {
         this.lir = lir;
         this.frameMapBuilder = frameMapBuilder;
+        this.compilationUnitName = compilationUnitName;
     }
 
     public LIR getLIR() {
@@ -69,4 +74,8 @@
         assert frameMap != null : "getFrameMap() can only be used after calling buildFrameMap()!";
         return frameMap;
     }
+
+    public String getCompilationUnitName() {
+        return compilationUnitName;
+    }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/profiling/MoveProfiling.java	Thu Mar 19 10:28:28 2015 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/profiling/MoveProfiling.java	Mon Mar 23 15:58:36 2015 +0100
@@ -136,12 +136,13 @@
             }
             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();
+            if (names.size() > 0) { // Don't pollute LIR when nothing has to be done
+                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();
+            }
         }
     }