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