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