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 
  24 
  25 package org.graalvm.compiler.hotspot;
  26 
  27 import static jdk.vm.ci.code.ValueUtil.isRegister;
  28 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant;
  29 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
  30 import static org.graalvm.compiler.nodes.debug.DynamicCounterNode.MAX_INCREMENT;
  31 import static org.graalvm.compiler.nodes.debug.DynamicCounterNode.MIN_INCREMENT;
  32 
  33 import java.util.Arrays;
  34 
  35 import jdk.internal.vm.compiler.collections.EconomicMap;
  36 import org.graalvm.compiler.asm.Assembler;
  37 import org.graalvm.compiler.core.common.NumUtil;
  38 import org.graalvm.compiler.debug.GraalError;
  39 import org.graalvm.compiler.hotspot.debug.BenchmarkCounters;
  40 import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
  41 import org.graalvm.compiler.lir.LIRInstruction;
  42 import org.graalvm.compiler.lir.LIRInstructionClass;
  43 
  44 import jdk.vm.ci.code.Register;
  45 import jdk.vm.ci.code.TargetDescription;
  46 import jdk.vm.ci.meta.JavaConstant;
  47 import jdk.vm.ci.meta.JavaKind;
  48 import jdk.vm.ci.meta.Value;
  49 
  50 public abstract class HotSpotCounterOp extends LIRInstruction {
  51     public static final LIRInstructionClass<HotSpotCounterOp> TYPE = LIRInstructionClass.create(HotSpotCounterOp.class);
  52 
  53     private final String[] names;
  54     private final String[] groups;
  55     protected final Register thread;
  56     protected final GraalHotSpotVMConfig config;
  57     @Alive({OperandFlag.CONST, OperandFlag.REG}) protected Value[] increments;
  58 
  59     public HotSpotCounterOp(LIRInstructionClass<? extends HotSpotCounterOp> c, String name, String group, Value increment, HotSpotRegistersProvider registers, GraalHotSpotVMConfig config) {
  60         this(c, new String[]{name}, new String[]{group}, new Value[]{increment}, registers, config);
  61     }
  62 
  63     public HotSpotCounterOp(LIRInstructionClass<? extends HotSpotCounterOp> c, String[] names, String[] groups, Value[] increments, HotSpotRegistersProvider registers, GraalHotSpotVMConfig config) {
  64         super(c);
  65 
  66         assert names.length == groups.length;
  67         assert groups.length == increments.length;
  68 
  69         this.names = names;
  70         this.groups = groups;
  71         this.increments = increments;
  72         this.thread = registers.getThreadRegister();
  73         this.config = config;
  74         checkIncrements();
  75     }
  76 
  77     private boolean checkIncrements() {
  78         for (int i = 0; i < increments.length; i++) {
  79             Value increment = increments[i];
  80             if (isJavaConstant(increment)) {
  81                 long incValue = asLong(asJavaConstant(increment));
  82                 if (incValue < MIN_INCREMENT || incValue > MAX_INCREMENT) {
  83                     String message = String.format("Benchmark counter %s:%s has increment out of range [%d .. %d]: %d", groups[i], names[i], MIN_INCREMENT, MAX_INCREMENT, incValue);
  84                     assert false : message;
  85                 }
  86             }
  87         }
  88         return true;
  89     }
  90 
  91     protected static int getDisplacementForLongIndex(TargetDescription target, long index) {
  92         long finalDisp = index * target.arch.getPlatformKind(JavaKind.Long).getSizeInBytes();
  93         if (!NumUtil.isInt(finalDisp)) {
  94             throw GraalError.unimplemented("cannot deal with indices that big: " + index);
  95         }
  96         return (int) finalDisp;
  97     }
  98 
  99     protected interface CounterProcedure {
 100         /**
 101          * Lambda interface for iterating over counters declared in this op.
 102          *
 103          * @param counterIndex Index in this CounterOp object.
 104          * @param increment Value for increment
 105          * @param displacement Displacement in bytes in the counter array
 106          */
 107         void apply(int counterIndex, Value increment, int displacement);
 108     }
 109 
 110     /**
 111      * Calls the {@link CounterProcedure} for each counter in ascending order of their displacement
 112      * in the counter array.
 113      *
 114      * @param proc The procedure to be called
 115      * @param target Target architecture (used to calculate the array displacements)
 116      */
 117     protected void forEachCounter(CounterProcedure proc, TargetDescription target) {
 118         if (names.length == 1) { // fast path
 119             int arrayIndex = getIndex(names[0], groups[0], increments[0]);
 120             int displacement = getDisplacementForLongIndex(target, arrayIndex);
 121             proc.apply(0, increments[0], displacement);
 122         } else { // Slow path with sort by displacements ascending
 123             int[] displacements = new int[names.length];
 124             EconomicMap<Integer, Integer> offsetMap = EconomicMap.create();
 125             for (int i = 0; i < names.length; i++) {
 126                 int arrayIndex = getIndex(names[i], groups[i], increments[i]);
 127                 displacements[i] = getDisplacementForLongIndex(target, arrayIndex);
 128                 offsetMap.put(displacements[i], i);
 129             }
 130             Arrays.sort(displacements);
 131             // Now apply in order
 132             for (int offset : displacements) {
 133                 int idx = offsetMap.get(offset);
 134                 proc.apply(idx, increments[idx], displacements[idx]);
 135             }
 136         }
 137     }
 138 
 139     protected int getIndex(String name, String group, Value increment) {
 140         if (isJavaConstant(increment)) {
 141             // get index for the counter
 142             return BenchmarkCounters.getIndexConstantIncrement(name, group, config, asLong(asJavaConstant(increment)));
 143         }
 144         assert isRegister(increment) : "Unexpected Value: " + increment;
 145         // get index for the counter
 146         return BenchmarkCounters.getIndex(name, group, config);
 147     }
 148 
 149     /**
 150      * Patches the increment value in the instruction emitted by this instruction. Use only, if
 151      * patching is needed after assembly.
 152      *
 153      * @param asm
 154      * @param increment
 155      */
 156     public void patchCounterIncrement(Assembler asm, int[] increment) {
 157         throw GraalError.unimplemented();
 158     }
 159 
 160     private static long asLong(JavaConstant value) {
 161         JavaKind kind = value.getJavaKind();
 162         switch (kind) {
 163             case Byte:
 164             case Short:
 165             case Char:
 166             case Int:
 167                 return value.asInt();
 168             case Long:
 169                 return value.asLong();
 170             default:
 171                 throw new IllegalArgumentException("not an integer kind: " + kind);
 172         }
 173     }
 174 
 175     protected static int asInt(JavaConstant value) {
 176         long l = asLong(value);
 177         if (!NumUtil.isInt(l)) {
 178             throw GraalError.shouldNotReachHere("value does not fit into int: " + l);
 179         }
 180         return (int) l;
 181     }
 182 
 183     public String[] getNames() {
 184         return names;
 185     }
 186 
 187     public String[] getGroups() {
 188         return groups;
 189     }
 190 }