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