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     record RawItemProfile<T>(int entries, T[] items, long[] counts, long totalCount) {}
450 
451     abstract static class AbstractTypeData extends CounterData {
452 
453         protected AbstractTypeData(VMState state, int tag, int staticSize) {
454             super(state, tag, staticSize);
455         }
456 
457         @Override
458         public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
459             return createTypeProfile(getNullSeen(data, position), getRawTypeProfile(data, position));
460         }
461 
462         private RawItemProfile<ResolvedJavaType> getRawTypeProfile(HotSpotMethodData data, int position) {
463             int typeProfileWidth = config.typeProfileWidth;
464 
465             ResolvedJavaType[] types = new ResolvedJavaType[typeProfileWidth];
466             long[] counts = new long[typeProfileWidth];
467             long totalCount = 0;
468             int entries = 0;
469 
470             outer: for (int i = 0; i < typeProfileWidth; i++) {
471                 HotSpotResolvedObjectTypeImpl receiverKlass = data.readKlass(position, getTypeOffset(i));
472                 if (receiverKlass != null) {
473                     HotSpotResolvedObjectTypeImpl klass = receiverKlass;
474                     long count = data.readUnsignedInt(position, getTypeCountOffset(i));
475                     /*
476                      * Because of races in the profile collection machinery it's possible for a
477                      * class to appear multiple times so merge them to make the profile look
478                      * rational.
479                      */
480                     for (int j = 0; j < entries; j++) {
481                         if (types[j].equals(klass)) {
482                             totalCount += count;
483                             counts[j] += count;
484                             continue outer;
485                         }
486                     }
487                     types[entries] = klass;
488                     totalCount += count;
489                     counts[entries] = count;
490                     entries++;
491                 }
492             }
493 
494             totalCount += getCounterValue(data, position);
495             return new RawItemProfile<>(entries, types, counts, totalCount);
496         }
497 
498         private JavaTypeProfile createTypeProfile(TriState nullSeen, RawItemProfile<ResolvedJavaType> profile) {
499             if (profile.entries <= 0 || profile.totalCount <= 0) {
500                 return null;
501             }
502 
503             ProfiledType[] ptypes = new ProfiledType[profile.entries];
504             double totalProbability = 0.0;
505             for (int i = 0; i < profile.entries; i++) {
506                 double p = profile.counts[i];
507                 p = p / profile.totalCount;
508                 totalProbability += p;
509                 ptypes[i] = new ProfiledType(profile.items[i], p);
510             }
511 
512             Arrays.sort(ptypes);
513 
514             double notRecordedTypeProbability = profile.entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
515             assert notRecordedTypeProbability == 0 || profile.entries == config.typeProfileWidth;
516             return new JavaTypeProfile(nullSeen, notRecordedTypeProbability, ptypes);
517         }
518 
519         private int getTypeOffset(int row) {
520             return state.typeDataFirstTypeOffset + row * state.typeDataRowSize;
521         }
522 
523         protected int getTypeCountOffset(int row) {
524             return state.typeDataFirstTypeCountOffset + row * state.typeDataRowSize;
525         }
526 
527         @Override
528         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
529             RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos);
530             TriState nullSeen = getNullSeen(data, pos);
531             TriState exceptionSeen = getExceptionSeen(data, pos);
532             sb.append(format("count(%d) null_seen(%s) exception_seen(%s) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen,
533                             profile.entries));
534             for (int i = 0; i < profile.entries; i++) {
535                 long count = profile.counts[i];
536                 sb.append(format("%n  %s (%d, %4.2f)", profile.items[i].toJavaName(), count, (double) count / profile.totalCount));
537             }
538             return sb;
539         }
540     }
541 
542     static class ReceiverTypeData extends AbstractTypeData {
543 
544         ReceiverTypeData(VMState state, int tag) {
545             super(state, tag, state.typeCheckDataSize);
546         }
547 
548         protected ReceiverTypeData(VMState state, int tag, int staticSize) {
549             super(state, tag, staticSize);
550         }
551 
552         @Override
553         public int getExecutionCount(HotSpotMethodData data, int position) {
554             return -1;
555         }
556     }
557 
558     static class VirtualCallData extends ReceiverTypeData {
559 
560         VirtualCallData(VMState state, int tag) {
561             super(state, tag, state.virtualCallDataSize);
562         }
563 
564         protected VirtualCallData(VMState state, int tag, int staticSize) {
565             super(state, tag, staticSize);
566         }
567 
568         @Override
569         public int getExecutionCount(HotSpotMethodData data, int position) {
570             final int typeProfileWidth = config.typeProfileWidth;
571 
572             long total = 0;
573             for (int i = 0; i < typeProfileWidth; i++) {
574                 total += data.readUnsignedInt(position, getTypeCountOffset(i));
575             }
576 
577             total += getCounterValue(data, position);
578             return VMState.truncateLongToInt(total);
579         }
580 
581         @Override
582         public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
583             return createMethodProfile(getRawMethodProfile(data, position));
584         }
585 
586         private RawItemProfile<ResolvedJavaMethod> getRawMethodProfile(HotSpotMethodData data, int position) {
587             int profileWidth = config.methodProfileWidth;
588 
589             ResolvedJavaMethod[] methods = new ResolvedJavaMethod[profileWidth];
590             long[] counts = new long[profileWidth];
591             long totalCount = 0;
592             int entries = 0;
593 
594             for (int i = 0; i < profileWidth; i++) {
595                 HotSpotResolvedJavaMethod method = data.readMethod(position, getMethodOffset(i));
596                 if (method != null) {
597                     methods[entries] = method;
598                     long count = data.readUnsignedInt(position, getMethodCountOffset(i));
599                     totalCount += count;
600                     counts[entries] = count;
601 
602                     entries++;
603                 }
604             }
605 
606             // Fixup the case of C1's inability to optimize profiling of a statically bindable call
607             // site. If it's a monomorphic call site, attribute all the counts to the first type (if
608             // any is recorded).
609             if (entries == 1) {
610                 counts[0] = totalCount;
611             }
612 
613             return new RawItemProfile<>(entries, methods, counts, totalCount);
614         }
615 
616         private JavaMethodProfile createMethodProfile(RawItemProfile<ResolvedJavaMethod> profile) {
617             if (profile.entries <= 0 || profile.totalCount <= 0) {
618                 return null;
619             }
620 
621             ProfiledMethod[] pmethods = new ProfiledMethod[profile.entries];
622             double totalProbability = 0.0;
623             for (int i = 0; i < profile.entries; i++) {
624                 double p = profile.counts[i];
625                 p = p / profile.totalCount;
626                 totalProbability += p;
627                 pmethods[i] = new ProfiledMethod(profile.items[i], p);
628             }
629 
630             Arrays.sort(pmethods);
631 
632             double notRecordedMethodProbability = profile.entries < config.methodProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
633             assert notRecordedMethodProbability == 0 || profile.entries == config.methodProfileWidth;
634             return new JavaMethodProfile(notRecordedMethodProbability, pmethods);
635         }
636 
637         private int getMethodOffset(int row) {
638             return state.virtualCallDataFirstMethodOffset + row * state.typeDataRowSize;
639         }
640 
641         private int getMethodCountOffset(int row) {
642             return state.virtualCallDataFirstMethodCountOffset + row * state.typeDataRowSize;
643         }
644 
645         @Override
646         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
647             RawItemProfile<ResolvedJavaMethod> profile = getRawMethodProfile(data, pos);
648             super.appendTo(sb.append(format("exception_seen(%s) ", getExceptionSeen(data, pos))), data, pos).append(format("%nmethod_entries(%d)", profile.entries));
649             for (int i = 0; i < profile.entries; i++) {
650                 long count = profile.counts[i];
651                 sb.append(format("%n  %s (%d, %4.2f)", profile.items[i].format("%H.%n(%p)"), count, (double) count / profile.totalCount));
652             }
653             return sb;
654         }
655     }
656 
657     static class VirtualCallTypeData extends VirtualCallData {
658 
659         VirtualCallTypeData(VMState state, int tag) {
660             super(state, tag, 0);
661         }
662 
663         @Override
664         protected int getDynamicSize(HotSpotMethodData data, int position) {
665             assert staticSize == 0;
666             return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.methodDataPointer, position);
667         }
668     }
669 
670     static class RetData extends CounterData {
671 
672         RetData(VMState state, int tag) {
673             super(state, tag, state.retDataSize);
674         }
675     }
676 
677     static class BranchData extends JumpData {
678 
679         BranchData(VMState state, int tag) {
680             super(state, tag, state.branchDataSize);
681         }
682 
683         @Override
684         public double getBranchTakenProbability(HotSpotMethodData data, int position) {
685             long takenCount = data.readUnsignedInt(position, state.takenCountOffset);
686             long notTakenCount = data.readUnsignedInt(position, state.notTakenCountOffset);
687             long total = takenCount + notTakenCount;
688 
689             return total <= 0 ? -1 : takenCount / (double) total;
690         }
691 
692         @Override
693         public int getExecutionCount(HotSpotMethodData data, int position) {
694             long count = data.readUnsignedInt(position, state.takenCountOffset) + data.readUnsignedInt(position, state.notTakenCountOffset);
695             return VMState.truncateLongToInt(count);
696         }
697 
698         @Override
699         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
700             long taken = data.readUnsignedInt(pos, state.takenCountOffset);
701             long notTaken = data.readUnsignedInt(pos, state.notTakenCountOffset);
702             double takenProbability = getBranchTakenProbability(data, pos);
703             return sb.append(format("taken(%d, %4.2f) not_taken(%d, %4.2f) displacement(%d)", taken, takenProbability, notTaken, 1.0D - takenProbability, getTakenDisplacement(data, pos)));
704         }
705     }
706 
707     static class ArrayData extends HotSpotMethodDataAccessor {
708 
709         ArrayData(VMState state, int tag, int staticSize) {
710             super(state, tag, staticSize);
711         }
712 
713         @Override
714         protected int getDynamicSize(HotSpotMethodData data, int position) {
715             return state.cellsToBytes(getLength(data, position));
716         }
717 
718         protected int getLength(HotSpotMethodData data, int position) {
719             return data.readInt(position, state.arrayDataLengthOffset);
720         }
721 
722         @Override
723         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
724             return sb.append(format("length(%d)", getLength(data, pos)));
725         }
726     }
727 
728     static class MultiBranchData extends ArrayData {
729 
730         MultiBranchData(VMState state, int tag) {
731             super(state, tag, state.multiBranchDataSize);
732         }
733 
734         @Override
735         public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
736             int arrayLength = getLength(data, position);
737             assert arrayLength > 0 : "switch must have at least the default case";
738             assert arrayLength % state.multiBranchDataRowSizeInCells == 0 : "array must have full rows";
739 
740             int length = arrayLength / state.multiBranchDataRowSizeInCells;
741             long totalCount = 0;
742             double[] result = new double[length];
743 
744             // default case is first in HotSpot but last for the compiler
745             long count = readCount(data, position, 0);
746             totalCount += count;
747             result[length - 1] = count;
748 
749             for (int i = 1; i < length; i++) {
750                 count = readCount(data, position, i);
751                 totalCount += count;
752                 result[i - 1] = count;
753             }
754 
755             if (totalCount <= 0) {
756                 return null;
757             } else {
758                 for (int i = 0; i < length; i++) {
759                     result[i] = result[i] / totalCount;
760                 }
761                 return result;
762             }
763         }
764 
765         private long readCount(HotSpotMethodData data, int position, int i) {
766             int offset;
767             long count;
768             offset = getCountOffset(i);
769             count = data.readUnsignedInt(position, offset);
770             return count;
771         }
772 
773         @Override
774         public int getExecutionCount(HotSpotMethodData data, int position) {
775             int arrayLength = getLength(data, position);
776             assert arrayLength > 0 : "switch must have at least the default case";
777             assert arrayLength % state.multiBranchDataRowSizeInCells == 0 : "array must have full rows";
778 
779             int length = arrayLength / state.multiBranchDataRowSizeInCells;
780             long totalCount = 0;
781             for (int i = 0; i < length; i++) {
782                 int offset = getCountOffset(i);
783                 totalCount += data.readUnsignedInt(position, offset);
784             }
785 
786             return VMState.truncateLongToInt(totalCount);
787         }
788 
789         private int getCountOffset(int index) {
790             return state.multiBranchDataFirstCountOffset + index * state.multiBranchDataRowSize;
791         }
792 
793         private int getDisplacementOffset(int index) {
794             return state.multiBranchDataFirstDisplacementOffset + index * state.multiBranchDataRowSize;
795         }
796 
797         @Override
798         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
799             int entries = getLength(data, pos) / state.multiBranchDataRowSizeInCells;
800             sb.append(format("entries(%d)", entries));
801             for (int i = 0; i < entries; i++) {
802                 sb.append(format("%n  %d: count(%d) displacement(%d)", i, data.readUnsignedInt(pos, getCountOffset(i)), data.readUnsignedInt(pos, getDisplacementOffset(i))));
803             }
804             return sb;
805         }
806     }
807 
808     static class ArgInfoData extends ArrayData {
809 
810         ArgInfoData(VMState state, int tag) {
811             super(state, tag, state.argInfoDataSize);
812         }
813     }
814 
815     static class UnknownProfileData extends HotSpotMethodDataAccessor {
816         UnknownProfileData(VMState state, int tag) {
817             super(state, tag, 0);
818         }
819 
820         @Override
821         protected int getDynamicSize(HotSpotMethodData data, int position) {
822             assert staticSize == 0;
823             return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.methodDataPointer, position);
824         }
825 
826         @Override
827         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
828             sb.append("unknown profile data with tag: " + tag);
829             return sb;
830         }
831     }
832 
833     public void setCompiledIRSize(int size) {
834         UNSAFE.putInt(methodDataPointer + state.config.methodDataIRSizeOffset, size);
835     }
836 
837     public int getCompiledIRSize() {
838         return UNSAFE.getInt(methodDataPointer + state.config.methodDataIRSizeOffset);
839     }
840 }