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