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