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