1 /*
   2  * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.hotspot;
  24 
  25 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant;
  26 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
  27 import static jdk.vm.ci.code.ValueUtil.isRegister;
  28 
  29 import java.util.Arrays;
  30 import java.util.HashMap;
  31 
  32 import org.graalvm.compiler.asm.Assembler;
  33 import org.graalvm.compiler.asm.NumUtil;
  34 import org.graalvm.compiler.debug.GraalError;
  35 import org.graalvm.compiler.hotspot.debug.BenchmarkCounters;
  36 import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
  37 import org.graalvm.compiler.lir.LIRInstruction;
  38 import org.graalvm.compiler.lir.LIRInstructionClass;
  39 
  40 import jdk.vm.ci.code.Register;
  41 import jdk.vm.ci.code.TargetDescription;
  42 import jdk.vm.ci.meta.JavaConstant;
  43 import jdk.vm.ci.meta.JavaKind;
  44 import jdk.vm.ci.meta.Value;
  45 
  46 public abstract class HotSpotCounterOp extends LIRInstruction {
  47     public static final LIRInstructionClass<HotSpotCounterOp> TYPE = LIRInstructionClass.create(HotSpotCounterOp.class);
  48 
  49     private final String[] names;
  50     private final String[] groups;
  51     protected final Register thread;
  52     protected final GraalHotSpotVMConfig config;
  53     @Alive({OperandFlag.CONST, OperandFlag.REG}) protected Value[] increments;
  54 
  55     public HotSpotCounterOp(LIRInstructionClass<? extends HotSpotCounterOp> c, String name, String group, Value increment, HotSpotRegistersProvider registers, GraalHotSpotVMConfig config) {
  56         this(c, new String[]{name}, new String[]{group}, new Value[]{increment}, registers, config);
  57     }
  58 
  59     public HotSpotCounterOp(LIRInstructionClass<? extends HotSpotCounterOp> c, String[] names, String[] groups, Value[] increments, HotSpotRegistersProvider registers, GraalHotSpotVMConfig config) {
  60         super(c);
  61 
  62         assert names.length == groups.length;
  63         assert groups.length == increments.length;
  64 
  65         this.names = names;
  66         this.groups = groups;
  67         this.increments = increments;
  68         this.thread = registers.getThreadRegister();
  69         this.config = config;
  70     }
  71 
  72     protected static int getDisplacementForLongIndex(TargetDescription target, long index) {
  73         long finalDisp = index * target.arch.getPlatformKind(JavaKind.Long).getSizeInBytes();
  74         if (!NumUtil.isInt(finalDisp)) {
  75             throw GraalError.unimplemented("cannot deal with indices that big: " + index);
  76         }
  77         return (int) finalDisp;
  78     }
  79 
  80     protected interface CounterProcedure {
  81         /**
  82          * Lambda interface for iterating over counters declared in this op.
  83          *
  84          * @param counterIndex Index in this CounterOp object.
  85          * @param increment Value for increment
  86          * @param displacement Displacement in bytes in the counter array
  87          */
  88         void apply(int counterIndex, Value increment, int displacement);
  89     }
  90 
  91     /**
  92      * Calls the {@link CounterProcedure} for each counter in ascending order of their displacement
  93      * in the counter array.
  94      *
  95      * @param proc The procedure to be called
  96      * @param target Target architecture (used to calculate the array displacements)
  97      */
  98     protected void forEachCounter(CounterProcedure proc, TargetDescription target) {
  99         if (names.length == 1) { // fast path
 100             int arrayIndex = getIndex(names[0], groups[0], increments[0]);
 101             int displacement = getDisplacementForLongIndex(target, arrayIndex);
 102             proc.apply(0, increments[0], displacement);
 103         } else { // Slow path with sort by displacements ascending
 104             int[] displacements = new int[names.length];
 105             HashMap<Integer, Integer> offsetMap = new HashMap<>(names.length);
 106             for (int i = 0; i < names.length; i++) {
 107                 int arrayIndex = getIndex(names[i], groups[i], increments[i]);
 108                 displacements[i] = getDisplacementForLongIndex(target, arrayIndex);
 109                 offsetMap.put(displacements[i], i);
 110             }
 111             Arrays.sort(displacements);
 112             // Now apply in order
 113             for (int offset : displacements) {
 114                 int idx = offsetMap.get(offset);
 115                 proc.apply(idx, increments[idx], displacements[idx]);
 116             }
 117         }
 118     }
 119 
 120     protected int getIndex(String name, String group, Value increment) {
 121         if (isJavaConstant(increment)) {
 122             // get index for the counter
 123             return BenchmarkCounters.getIndexConstantIncrement(name, group, config, asLong(asJavaConstant(increment)));
 124         }
 125         assert isRegister(increment) : "Unexpected Value: " + increment;
 126         // get index for the counter
 127         return BenchmarkCounters.getIndex(name, group, config);
 128     }
 129 
 130     /**
 131      * Patches the increment value in the instruction emitted by this instruction. Use only, if
 132      * patching is needed after assembly.
 133      *
 134      * @param asm
 135      * @param increment
 136      */
 137     public void patchCounterIncrement(Assembler asm, int[] increment) {
 138         throw GraalError.unimplemented();
 139     }
 140 
 141     private static long asLong(JavaConstant value) {
 142         JavaKind kind = value.getJavaKind();
 143         switch (kind) {
 144             case Byte:
 145             case Short:
 146             case Char:
 147             case Int:
 148                 return value.asInt();
 149             case Long:
 150                 return value.asLong();
 151             default:
 152                 throw new IllegalArgumentException("not an integer kind: " + kind);
 153         }
 154     }
 155 
 156     protected static int asInt(JavaConstant value) {
 157         long l = asLong(value);
 158         if (!NumUtil.isInt(l)) {
 159             throw GraalError.shouldNotReachHere("value does not fit into int: " + l);
 160         }
 161         return (int) l;
 162     }
 163 
 164     public String[] getNames() {
 165         return names;
 166     }
 167 
 168     public String[] getGroups() {
 169         return groups;
 170     }
 171 }