1 /*
  2  * Copyright (c) 2012, 2024, 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 jdk.vm.ci.hotspot;
 24 
 25 import static java.lang.String.format;
 26 import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
 27 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
 28 import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
 29 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
 30 
 31 import java.util.Arrays;
 32 
 33 import jdk.vm.ci.common.NativeImageReinitialize;
 34 import jdk.internal.misc.Unsafe;
 35 import jdk.vm.ci.meta.DeoptimizationReason;
 36 import jdk.vm.ci.meta.JavaMethodProfile;
 37 import jdk.vm.ci.meta.JavaMethodProfile.ProfiledMethod;
 38 import jdk.vm.ci.meta.JavaTypeProfile;
 39 import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType;
 40 import jdk.vm.ci.meta.ResolvedJavaMethod;
 41 import jdk.vm.ci.meta.ResolvedJavaType;
 42 import jdk.vm.ci.meta.TriState;
 43 
 44 /**
 45  * Access to a HotSpot {@code MethodData} structure (defined in methodData.hpp).
 46  */
 47 final class HotSpotMethodData implements MetaspaceObject {
 48 
 49     /**
 50      * VM state that can be reset when building an AOT image.
 51      */
 52     static final class VMState {
 53         final HotSpotVMConfig config = config();
 54         final HotSpotMethodDataAccessor noDataNoExceptionAccessor = new NoMethodData(this, config.dataLayoutNoTag, TriState.FALSE);
 55         final HotSpotMethodDataAccessor noDataExceptionPossiblyNotRecordedAccessor = new NoMethodData(this, config.dataLayoutNoTag, TriState.UNKNOWN);
 56         final int noDataSize = cellIndexToOffset(0);
 57         final int bitDataSize = cellIndexToOffset(0);
 58         final int bitDataNullSeenFlag = 1 << config.bitDataNullSeenFlag;
 59         final int counterDataSize = cellIndexToOffset(1);
 60         final int counterDataCountOffset = cellIndexToOffset(config.methodDataCountOffset);
 61         final int jumpDataSize = cellIndexToOffset(2);
 62         final int takenCountOffset = cellIndexToOffset(config.jumpDataTakenOffset);
 63         final int takenDisplacementOffset = cellIndexToOffset(config.jumpDataDisplacementOffset);
 64         final int typeDataRowSize = cellsToBytes(config.receiverTypeDataReceiverTypeRowCellCount);
 65 
 66         final int typeDataFirstTypeOffset = cellIndexToOffset(config.receiverTypeDataReceiver0Offset);
 67         final int typeDataFirstTypeCountOffset = cellIndexToOffset(config.receiverTypeDataCount0Offset);
 68 
 69         final int typeCheckDataSize = cellIndexToOffset(1) + typeDataRowSize * config.typeProfileWidth;
 70         final int virtualCallDataSize = cellIndexToOffset(1) + typeDataRowSize * (config.typeProfileWidth + config.methodProfileWidth);
 71         final int virtualCallDataFirstMethodOffset = typeDataFirstTypeOffset + typeDataRowSize * config.typeProfileWidth;
 72         final int virtualCallDataFirstMethodCountOffset = typeDataFirstTypeCountOffset + typeDataRowSize * config.typeProfileWidth;
 73 
 74         final int retDataRowSize = cellsToBytes(3);
 75         final int retDataSize = cellIndexToOffset(1) + retDataRowSize * config.bciProfileWidth;
 76 
 77         final int branchDataSize = cellIndexToOffset(3);
 78         final int notTakenCountOffset = cellIndexToOffset(config.branchDataNotTakenOffset);
 79 
 80         final int arrayDataLengthOffset = cellIndexToOffset(config.arrayDataArrayLenOffset);
 81         final int arrayDataStartOffset = cellIndexToOffset(config.arrayDataArrayStartOffset);
 82 
 83         final int multiBranchDataSize = cellIndexToOffset(1);
 84         final int multiBranchDataRowSizeInCells = config.multiBranchDataPerCaseCellCount;
 85         final int multiBranchDataRowSize = cellsToBytes(multiBranchDataRowSizeInCells);
 86         final int multiBranchDataFirstCountOffset = arrayDataStartOffset + cellsToBytes(0);
 87         final int multiBranchDataFirstDisplacementOffset = arrayDataStartOffset + cellsToBytes(1);
 88 
 89         final int argInfoDataSize = cellIndexToOffset(1);
 90 
 91         // sorted by tag
 92         // @formatter:off
 93         final HotSpotMethodDataAccessor[] profileDataAccessors = {
 94             null,
 95             new BitData(this, config.dataLayoutBitDataTag),
 96             new CounterData(this, config.dataLayoutCounterDataTag),
 97             new JumpData(this, config.dataLayoutJumpDataTag),
 98             new ReceiverTypeData(this, config.dataLayoutReceiverTypeDataTag),
 99             new VirtualCallData(this, config.dataLayoutVirtualCallDataTag),
100             new RetData(this, config.dataLayoutRetDataTag),
101             new BranchData(this, config.dataLayoutBranchDataTag),
102             new MultiBranchData(this, config.dataLayoutMultiBranchDataTag),
103             new ArgInfoData(this, config.dataLayoutArgInfoDataTag),
104             new UnknownProfileData(this, config.dataLayoutCallTypeDataTag),
105             new VirtualCallTypeData(this, config.dataLayoutVirtualCallTypeDataTag),
106             new UnknownProfileData(this, config.dataLayoutParametersTypeDataTag),
107             new UnknownProfileData(this, config.dataLayoutSpeculativeTrapDataTag),
108             new UnknownProfileData(this, config.dataLayoutArrayStoreDataTag),
109             new UnknownProfileData(this, config.dataLayoutArrayLoadDataTag),
110             new UnknownProfileData(this, config.dataLayoutACmpDataTag),
111         };
112         // @formatter:on
113 
114         private boolean checkAccessorTags() {
115             int expectedTag = 0;
116             for (HotSpotMethodDataAccessor accessor : profileDataAccessors) {
117                 if (expectedTag == 0) {
118                     assert accessor == null;
119                 } else {
120                     assert accessor.tag == expectedTag : expectedTag + " != " + accessor.tag + " " + accessor;
121                 }
122                 expectedTag++;
123             }
124             return true;
125         }
126 
127         private VMState() {
128             assert checkAccessorTags();
129         }
130 
131         private static int truncateLongToInt(long value) {
132             return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) value;
133         }
134 
135         private int computeFullOffset(int position, int offsetInBytes) {
136             return config.methodDataOopDataOffset + position + offsetInBytes;
137         }
138 
139         private int cellIndexToOffset(int cells) {
140             return config.dataLayoutHeaderSize + cellsToBytes(cells);
141         }
142 
143         private int cellsToBytes(int cells) {
144             return cells * config.dataLayoutCellSize;
145         }
146 
147         /**
148          * Singleton instance lazily initialized via double-checked locking.
149          */
150         @NativeImageReinitialize private static volatile VMState instance;
151 
152         static VMState instance() {
153             VMState result = instance;
154             if (result == null) {
155                 synchronized (VMState.class) {
156                     result = instance;
157                     if (result == null) {
158                         instance = result = new VMState();
159                     }
160                 }
161             }
162             return result;
163         }
164     }
165 
166     /**
167      * A {@code MethodData*} value.
168      */
169     final long methodDataPointer;
170     private final HotSpotResolvedJavaMethodImpl method;
171     private final VMState state;
172 
173     HotSpotMethodData(long methodDataPointer, HotSpotResolvedJavaMethodImpl method) {
174         this.methodDataPointer = methodDataPointer;
175         this.method = method;
176         this.state = VMState.instance();
177     }
178 
179     @Override
180     public long getMetaspacePointer() {
181         return methodDataPointer;
182     }
183 
184     /**
185      * @return value of the MethodData::_data_size field
186      */
187     private int normalDataSize() {
188         return UNSAFE.getInt(methodDataPointer + state.config.methodDataDataSize);
189     }
190 
191     /**
192      * Returns the size of the extra data records. This method does the same calculation as
193      * MethodData::extra_data_size().
194      *
195      * @return size of extra data records
196      */
197     private int extraDataSize() {
198         final int extraDataBase = state.config.methodDataOopDataOffset + normalDataSize();
199         final int extraDataLimit = UNSAFE.getInt(methodDataPointer + state.config.methodDataSize);
200         return extraDataLimit - extraDataBase;
201     }
202 
203     public boolean hasNormalData() {
204         return normalDataSize() > 0;
205     }
206 
207     /**
208      * Return true if there is an extra data section and the first tag is non-zero.
209      */
210     public boolean hasExtraData() {
211         return extraDataSize() > 0 && HotSpotMethodDataAccessor.readTag(state.config, this, getExtraDataBeginOffset()) != 0;
212     }
213 
214     private int getExtraDataBeginOffset() {
215         return normalDataSize();
216     }
217 
218     public boolean isWithin(int position) {
219         return position >= 0 && position < normalDataSize();
220     }
221 
222     public int getDeoptimizationCount(DeoptimizationReason reason) {
223         HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess();
224         int reasonIndex = metaAccess.convertDeoptReason(reason);
225         return UNSAFE.getByte(methodDataPointer + state.config.methodDataOopTrapHistoryOffset + reasonIndex) & 0xFF;
226     }
227 
228     public int getOSRDeoptimizationCount(DeoptimizationReason reason) {
229         HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess();
230         int reasonIndex = metaAccess.convertDeoptReason(reason);
231         return UNSAFE.getByte(methodDataPointer + state.config.methodDataOopTrapHistoryOffset + state.config.deoptReasonOSROffset + reasonIndex) & 0xFF;
232     }
233 
234     public int getDecompileCount() {
235         return UNSAFE.getInt(methodDataPointer + state.config.methodDataDecompiles);
236     }
237 
238     public int getOverflowRecompileCount() {
239         return UNSAFE.getInt(methodDataPointer + state.config.methodDataOverflowRecompiles);
240     }
241 
242     public int getOverflowTrapCount() {
243         return UNSAFE.getInt(methodDataPointer + state.config.methodDataOverflowTraps);
244     }
245 
246     public HotSpotMethodDataAccessor getNormalData(int position) {
247         if (position >= normalDataSize()) {
248             return null;
249         }
250 
251         return getData(position);
252     }
253 
254     public static HotSpotMethodDataAccessor getNoDataAccessor(boolean exceptionPossiblyNotRecorded) {
255         if (exceptionPossiblyNotRecorded) {
256             return VMState.instance().noDataExceptionPossiblyNotRecordedAccessor;
257         } else {
258             return VMState.instance().noDataNoExceptionAccessor;
259         }
260     }
261 
262     private HotSpotMethodDataAccessor getData(int position) {
263         assert position >= 0 : "out of bounds";
264         final int tag = HotSpotMethodDataAccessor.readTag(state.config, this, position);
265         HotSpotMethodDataAccessor accessor = state.profileDataAccessors[tag];
266         assert accessor == null || accessor.getTag() == tag : "wrong data accessor " + accessor + " for tag " + tag;
267         return accessor;
268     }
269 
270     int readUnsignedByte(int position, int offsetInBytes) {
271         long fullOffsetInBytes = state.computeFullOffset(position, offsetInBytes);
272         return UNSAFE.getByte(methodDataPointer + fullOffsetInBytes) & 0xFF;
273     }
274 
275     int readUnsignedShort(int position, int offsetInBytes) {
276         long fullOffsetInBytes = state.computeFullOffset(position, offsetInBytes);
277         return UNSAFE.getShort(methodDataPointer + fullOffsetInBytes) & 0xFFFF;
278     }
279 
280     /**
281      * Since the values are stored in cells (platform words) this method uses
282      * {@link Unsafe#getAddress} to read the right value on both little and big endian machines.
283      */
284     private long readUnsignedInt(int position, int offsetInBytes) {
285         long fullOffsetInBytes = state.computeFullOffset(position, offsetInBytes);
286         return UNSAFE.getAddress(methodDataPointer + fullOffsetInBytes) & 0xFFFFFFFFL;
287     }
288 
289     private int readUnsignedIntAsSignedInt(int position, int offsetInBytes) {
290         long value = readUnsignedInt(position, offsetInBytes);
291         return VMState.truncateLongToInt(value);
292     }
293 
294     /**
295      * Since the values are stored in cells (platform words) this method uses
296      * {@link Unsafe#getAddress} to read the right value on both little and big endian machines.
297      */
298     private int readInt(int position, int offsetInBytes) {
299         long fullOffsetInBytes = state.computeFullOffset(position, offsetInBytes);
300         return (int) UNSAFE.getAddress(methodDataPointer + fullOffsetInBytes);
301     }
302 
303     private HotSpotResolvedJavaMethod readMethod(int position, int offsetInBytes) {
304         long fullOffsetInBytes = state.computeFullOffset(position, offsetInBytes);
305         return compilerToVM().getResolvedJavaMethod(null, methodDataPointer + fullOffsetInBytes);
306     }
307 
308     private HotSpotResolvedObjectTypeImpl readKlass(int position, int offsetInBytes) {
309         long fullOffsetInBytes = state.computeFullOffset(position, offsetInBytes);
310         return compilerToVM().getResolvedJavaType(this, fullOffsetInBytes);
311     }
312 
313     /**
314      * Returns whether profiling ran long enough that the profile information is mature. Other
315      * informational data will still be valid even if the profile isn't mature.
316      */
317     public boolean isProfileMature() {
318         return runtime().getCompilerToVM().isMature(methodDataPointer);
319     }
320 
321     @Override
322     public String toString() {
323         StringBuilder sb = new StringBuilder();
324         String nl = System.lineSeparator();
325         String nlIndent = String.format("%n%38s", "");
326         sb.append("Raw method data for ");
327         sb.append(method.format("%H.%n(%p)"));
328         sb.append(":");
329         sb.append(nl);
330         sb.append(String.format("nof_decompiles(%d) nof_overflow_recompiles(%d) nof_overflow_traps(%d)%n",
331                         getDecompileCount(), getOverflowRecompileCount(), getOverflowTrapCount()));
332         if (hasNormalData()) {
333             int pos = 0;
334             HotSpotMethodDataAccessor data;
335             while ((data = getNormalData(pos)) != null) {
336                 if (pos != 0) {
337                     sb.append(nl);
338                 }
339                 int bci = data.getBCI(this, pos);
340                 sb.append(String.format("%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName()));
341                 sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent));
342                 pos = pos + data.getSize(this, pos);
343             }
344         }
345 
346         return sb.toString();
347     }
348 
349     static class NoMethodData extends HotSpotMethodDataAccessor {
350 
351         private final TriState exceptionSeen;
352 
353         protected NoMethodData(VMState state, int tag, TriState exceptionSeen) {
354             super(state, tag, state.noDataSize);
355             this.exceptionSeen = exceptionSeen;
356         }
357 
358         @Override
359         public int getBCI(HotSpotMethodData data, int position) {
360             return -1;
361         }
362 
363         @Override
364         public TriState getExceptionSeen(HotSpotMethodData data, int position) {
365             return exceptionSeen;
366         }
367 
368         @Override
369         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
370             return sb;
371         }
372     }
373 
374     static class BitData extends HotSpotMethodDataAccessor {
375 
376         private BitData(VMState state, int tag) {
377             super(state, tag, state.bitDataSize);
378         }
379 
380         protected BitData(VMState state, int tag, int staticSize) {
381             super(state, tag, staticSize);
382         }
383 
384         @Override
385         public TriState getNullSeen(HotSpotMethodData data, int position) {
386             return TriState.get((getFlags(data, position) & state.bitDataNullSeenFlag) != 0);
387         }
388 
389         @Override
390         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
391             return sb.append(format("exception_seen(%s)", getExceptionSeen(data, pos)));
392         }
393     }
394 
395     static class CounterData extends BitData {
396 
397         CounterData(VMState state, int tag) {
398             super(state, tag, state.counterDataSize);
399         }
400 
401         protected CounterData(VMState state, int tag, int staticSize) {
402             super(state, tag, staticSize);
403         }
404 
405         @Override
406         public int getExecutionCount(HotSpotMethodData data, int position) {
407             return getCounterValue(data, position);
408         }
409 
410         protected int getCounterValue(HotSpotMethodData data, int position) {
411             return data.readUnsignedIntAsSignedInt(position, state.counterDataCountOffset);
412         }
413 
414         @Override
415         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
416             return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos)));
417         }
418     }
419 
420     static class JumpData extends HotSpotMethodDataAccessor {
421 
422         JumpData(VMState state, int tag) {
423             super(state, tag, state.jumpDataSize);
424         }
425 
426         protected JumpData(VMState state, int tag, int staticSize) {
427             super(state, tag, staticSize);
428         }
429 
430         @Override
431         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
432             return getExecutionCount(data, position) != 0 ? 1 : 0;
433         }
434 
435         @Override
436         public int getExecutionCount(HotSpotMethodData data, int position) {
437             return data.readUnsignedIntAsSignedInt(position, state.takenCountOffset);
438         }
439 
440         public int getTakenDisplacement(HotSpotMethodData data, int position) {
441             return data.readInt(position, state.takenDisplacementOffset);
442         }
443 
444         @Override
445         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
446             return sb.append(format("taken(%d) displacement(%d)", getExecutionCount(data, pos), getTakenDisplacement(data, pos)));
447         }
448     }
449 
450     static class RawItemProfile<T> {
451         final int entries;
452         final T[] items;
453         final long[] counts;
454         final long totalCount;
455 
456         RawItemProfile(int entries, T[] items, long[] counts, long totalCount) {
457             this.entries = entries;
458             this.items = items;
459             this.counts = counts;
460             this.totalCount = totalCount;
461         }
462     }
463 
464     abstract static class AbstractTypeData extends CounterData {
465 
466         protected AbstractTypeData(VMState state, int tag, int staticSize) {
467             super(state, tag, staticSize);
468         }
469 
470         @Override
471         public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
472             return createTypeProfile(getNullSeen(data, position), getRawTypeProfile(data, position));
473         }
474 
475         private RawItemProfile<ResolvedJavaType> getRawTypeProfile(HotSpotMethodData data, int position) {
476             int typeProfileWidth = config.typeProfileWidth;
477 
478             ResolvedJavaType[] types = new ResolvedJavaType[typeProfileWidth];
479             long[] counts = new long[typeProfileWidth];
480             long totalCount = 0;
481             int entries = 0;
482 
483             outer: for (int i = 0; i < typeProfileWidth; i++) {
484                 HotSpotResolvedObjectTypeImpl receiverKlass = data.readKlass(position, getTypeOffset(i));
485                 if (receiverKlass != null) {
486                     HotSpotResolvedObjectTypeImpl klass = receiverKlass;
487                     long count = data.readUnsignedInt(position, getTypeCountOffset(i));
488                     /*
489                      * Because of races in the profile collection machinery it's possible for a
490                      * class to appear multiple times so merge them to make the profile look
491                      * rational.
492                      */
493                     for (int j = 0; j < entries; j++) {
494                         if (types[j].equals(klass)) {
495                             totalCount += count;
496                             counts[j] += count;
497                             continue outer;
498                         }
499                     }
500                     types[entries] = klass;
501                     totalCount += count;
502                     counts[entries] = count;
503                     entries++;
504                 }
505             }
506 
507             totalCount += getCounterValue(data, position);
508             return new RawItemProfile<>(entries, types, counts, totalCount);
509         }
510 
511         private JavaTypeProfile createTypeProfile(TriState nullSeen, RawItemProfile<ResolvedJavaType> profile) {
512             if (profile.entries <= 0 || profile.totalCount <= 0) {
513                 return null;
514             }
515 
516             ProfiledType[] ptypes = new ProfiledType[profile.entries];
517             double totalProbability = 0.0;
518             for (int i = 0; i < profile.entries; i++) {
519                 double p = profile.counts[i];
520                 p = p / profile.totalCount;
521                 totalProbability += p;
522                 ptypes[i] = new ProfiledType(profile.items[i], p);
523             }
524 
525             Arrays.sort(ptypes);
526 
527             double notRecordedTypeProbability = profile.entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
528             assert notRecordedTypeProbability == 0 || profile.entries == config.typeProfileWidth;
529             return new JavaTypeProfile(nullSeen, notRecordedTypeProbability, ptypes);
530         }
531 
532         private int getTypeOffset(int row) {
533             return state.typeDataFirstTypeOffset + row * state.typeDataRowSize;
534         }
535 
536         protected int getTypeCountOffset(int row) {
537             return state.typeDataFirstTypeCountOffset + row * state.typeDataRowSize;
538         }
539 
540         @Override
541         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
542             RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos);
543             TriState nullSeen = getNullSeen(data, pos);
544             TriState exceptionSeen = getExceptionSeen(data, pos);
545             sb.append(format("count(%d) null_seen(%s) exception_seen(%s) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen,
546                             profile.entries));
547             for (int i = 0; i < profile.entries; i++) {
548                 long count = profile.counts[i];
549                 sb.append(format("%n  %s (%d, %4.2f)", profile.items[i].toJavaName(), count, (double) count / profile.totalCount));
550             }
551             return sb;
552         }
553     }
554 
555     static class ReceiverTypeData extends AbstractTypeData {
556 
557         ReceiverTypeData(VMState state, int tag) {
558             super(state, tag, state.typeCheckDataSize);
559         }
560 
561         protected ReceiverTypeData(VMState state, int tag, int staticSize) {
562             super(state, tag, staticSize);
563         }
564 
565         @Override
566         public int getExecutionCount(HotSpotMethodData data, int position) {
567             return -1;
568         }
569     }
570 
571     static class VirtualCallData extends ReceiverTypeData {
572 
573         VirtualCallData(VMState state, int tag) {
574             super(state, tag, state.virtualCallDataSize);
575         }
576 
577         protected VirtualCallData(VMState state, int tag, int staticSize) {
578             super(state, tag, staticSize);
579         }
580 
581         @Override
582         public int getExecutionCount(HotSpotMethodData data, int position) {
583             final int typeProfileWidth = config.typeProfileWidth;
584 
585             long total = 0;
586             for (int i = 0; i < typeProfileWidth; i++) {
587                 total += data.readUnsignedInt(position, getTypeCountOffset(i));
588             }
589 
590             total += getCounterValue(data, position);
591             return VMState.truncateLongToInt(total);
592         }
593 
594         @Override
595         public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
596             return createMethodProfile(getRawMethodProfile(data, position));
597         }
598 
599         private RawItemProfile<ResolvedJavaMethod> getRawMethodProfile(HotSpotMethodData data, int position) {
600             int profileWidth = config.methodProfileWidth;
601 
602             ResolvedJavaMethod[] methods = new ResolvedJavaMethod[profileWidth];
603             long[] counts = new long[profileWidth];
604             long totalCount = 0;
605             int entries = 0;
606 
607             for (int i = 0; i < profileWidth; i++) {
608                 HotSpotResolvedJavaMethod method = data.readMethod(position, getMethodOffset(i));
609                 if (method != null) {
610                     methods[entries] = method;
611                     long count = data.readUnsignedInt(position, getMethodCountOffset(i));
612                     totalCount += count;
613                     counts[entries] = count;
614 
615                     entries++;
616                 }
617             }
618 
619             // Fixup the case of C1's inability to optimize profiling of a statically bindable call
620             // site. If it's a monomorphic call site, attribute all the counts to the first type (if
621             // any is recorded).
622             if (entries == 1) {
623                 counts[0] = totalCount;
624             }
625 
626             return new RawItemProfile<>(entries, methods, counts, totalCount);
627         }
628 
629         private JavaMethodProfile createMethodProfile(RawItemProfile<ResolvedJavaMethod> profile) {
630             if (profile.entries <= 0 || profile.totalCount <= 0) {
631                 return null;
632             }
633 
634             ProfiledMethod[] pmethods = new ProfiledMethod[profile.entries];
635             double totalProbability = 0.0;
636             for (int i = 0; i < profile.entries; i++) {
637                 double p = profile.counts[i];
638                 p = p / profile.totalCount;
639                 totalProbability += p;
640                 pmethods[i] = new ProfiledMethod(profile.items[i], p);
641             }
642 
643             Arrays.sort(pmethods);
644 
645             double notRecordedMethodProbability = profile.entries < config.methodProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
646             assert notRecordedMethodProbability == 0 || profile.entries == config.methodProfileWidth;
647             return new JavaMethodProfile(notRecordedMethodProbability, pmethods);
648         }
649 
650         private int getMethodOffset(int row) {
651             return state.virtualCallDataFirstMethodOffset + row * state.typeDataRowSize;
652         }
653 
654         private int getMethodCountOffset(int row) {
655             return state.virtualCallDataFirstMethodCountOffset + row * state.typeDataRowSize;
656         }
657 
658         @Override
659         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
660             RawItemProfile<ResolvedJavaMethod> profile = getRawMethodProfile(data, pos);
661             super.appendTo(sb.append(format("exception_seen(%s) ", getExceptionSeen(data, pos))), data, pos).append(format("%nmethod_entries(%d)", profile.entries));
662             for (int i = 0; i < profile.entries; i++) {
663                 long count = profile.counts[i];
664                 sb.append(format("%n  %s (%d, %4.2f)", profile.items[i].format("%H.%n(%p)"), count, (double) count / profile.totalCount));
665             }
666             return sb;
667         }
668     }
669 
670     static class VirtualCallTypeData extends VirtualCallData {
671 
672         VirtualCallTypeData(VMState state, int tag) {
673             super(state, tag, 0);
674         }
675 
676         @Override
677         protected int getDynamicSize(HotSpotMethodData data, int position) {
678             assert staticSize == 0;
679             return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.methodDataPointer, position);
680         }
681     }
682 
683     static class RetData extends CounterData {
684 
685         RetData(VMState state, int tag) {
686             super(state, tag, state.retDataSize);
687         }
688     }
689 
690     static class BranchData extends JumpData {
691 
692         BranchData(VMState state, int tag) {
693             super(state, tag, state.branchDataSize);
694         }
695 
696         @Override
697         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
698             long takenCount = data.readUnsignedInt(position, state.takenCountOffset);
699             long notTakenCount = data.readUnsignedInt(position, state.notTakenCountOffset);
700             long total = takenCount + notTakenCount;
701 
702             return total <= 0 ? -1 : takenCount / (double) total;
703         }
704 
705         @Override
706         public int getExecutionCount(HotSpotMethodData data, int position) {
707             long count = data.readUnsignedInt(position, state.takenCountOffset) + data.readUnsignedInt(position, state.notTakenCountOffset);
708             return VMState.truncateLongToInt(count);
709         }
710 
711         @Override
712         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
713             long taken = data.readUnsignedInt(pos, state.takenCountOffset);
714             long notTaken = data.readUnsignedInt(pos, state.notTakenCountOffset);
715             double takenProbability = getBranchTakenProbability(data, pos);
716             return sb.append(format("taken(%d, %4.2f) not_taken(%d, %4.2f) displacement(%d)", taken, takenProbability, notTaken, 1.0D - takenProbability, getTakenDisplacement(data, pos)));
717         }
718     }
719 
720     static class ArrayData extends HotSpotMethodDataAccessor {
721 
722         ArrayData(VMState state, int tag, int staticSize) {
723             super(state, tag, staticSize);
724         }
725 
726         @Override
727         protected int getDynamicSize(HotSpotMethodData data, int position) {
728             return state.cellsToBytes(getLength(data, position));
729         }
730 
731         protected int getLength(HotSpotMethodData data, int position) {
732             return data.readInt(position, state.arrayDataLengthOffset);
733         }
734 
735         @Override
736         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
737             return sb.append(format("length(%d)", getLength(data, pos)));
738         }
739     }
740 
741     static class MultiBranchData extends ArrayData {
742 
743         MultiBranchData(VMState state, int tag) {
744             super(state, tag, state.multiBranchDataSize);
745         }
746 
747         @Override
748         public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
749             int arrayLength = getLength(data, position);
750             assert arrayLength > 0 : "switch must have at least the default case";
751             assert arrayLength % state.multiBranchDataRowSizeInCells == 0 : "array must have full rows";
752 
753             int length = arrayLength / state.multiBranchDataRowSizeInCells;
754             long totalCount = 0;
755             double[] result = new double[length];
756 
757             // default case is first in HotSpot but last for the compiler
758             long count = readCount(data, position, 0);
759             totalCount += count;
760             result[length - 1] = count;
761 
762             for (int i = 1; i < length; i++) {
763                 count = readCount(data, position, i);
764                 totalCount += count;
765                 result[i - 1] = count;
766             }
767 
768             if (totalCount <= 0) {
769                 return null;
770             } else {
771                 for (int i = 0; i < length; i++) {
772                     result[i] = result[i] / totalCount;
773                 }
774                 return result;
775             }
776         }
777 
778         private long readCount(HotSpotMethodData data, int position, int i) {
779             int offset;
780             long count;
781             offset = getCountOffset(i);
782             count = data.readUnsignedInt(position, offset);
783             return count;
784         }
785 
786         @Override
787         public int getExecutionCount(HotSpotMethodData data, int position) {
788             int arrayLength = getLength(data, position);
789             assert arrayLength > 0 : "switch must have at least the default case";
790             assert arrayLength % state.multiBranchDataRowSizeInCells == 0 : "array must have full rows";
791 
792             int length = arrayLength / state.multiBranchDataRowSizeInCells;
793             long totalCount = 0;
794             for (int i = 0; i < length; i++) {
795                 int offset = getCountOffset(i);
796                 totalCount += data.readUnsignedInt(position, offset);
797             }
798 
799             return VMState.truncateLongToInt(totalCount);
800         }
801 
802         private int getCountOffset(int index) {
803             return state.multiBranchDataFirstCountOffset + index * state.multiBranchDataRowSize;
804         }
805 
806         private int getDisplacementOffset(int index) {
807             return state.multiBranchDataFirstDisplacementOffset + index * state.multiBranchDataRowSize;
808         }
809 
810         @Override
811         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
812             int entries = getLength(data, pos) / state.multiBranchDataRowSizeInCells;
813             sb.append(format("entries(%d)", entries));
814             for (int i = 0; i < entries; i++) {
815                 sb.append(format("%n  %d: count(%d) displacement(%d)", i, data.readUnsignedInt(pos, getCountOffset(i)), data.readUnsignedInt(pos, getDisplacementOffset(i))));
816             }
817             return sb;
818         }
819     }
820 
821     static class ArgInfoData extends ArrayData {
822 
823         ArgInfoData(VMState state, int tag) {
824             super(state, tag, state.argInfoDataSize);
825         }
826     }
827 
828     static class UnknownProfileData extends HotSpotMethodDataAccessor {
829         UnknownProfileData(VMState state, int tag) {
830             super(state, tag, 0);
831         }
832 
833         @Override
834         protected int getDynamicSize(HotSpotMethodData data, int position) {
835             assert staticSize == 0;
836             return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.methodDataPointer, position);
837         }
838 
839         @Override
840         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
841             sb.append("unknown profile data with tag: " + tag);
842             return sb;
843         }
844     }
845 
846     public void setCompiledIRSize(int size) {
847         UNSAFE.putInt(methodDataPointer + state.config.methodDataIRSizeOffset, size);
848     }
849 
850     public int getCompiledIRSize() {
851         return UNSAFE.getInt(methodDataPointer + state.config.methodDataIRSizeOffset);
852     }
853 }