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