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