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