package jdk.vm.ci.hotspot;
import static java.lang.String.format;
import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
import java.util.Arrays;
import jdk.internal.misc.Unsafe;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaMethodProfile;
import jdk.vm.ci.meta.JavaMethodProfile.ProfiledMethod;
import jdk.vm.ci.meta.JavaTypeProfile;
import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.TriState;
final class HotSpotMethodData {
static final HotSpotVMConfig config = config();
static final HotSpotMethodDataAccessor NO_DATA_NO_EXCEPTION_ACCESSOR = new NoMethodData(config, config.dataLayoutNoTag, TriState.FALSE);
static final HotSpotMethodDataAccessor NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR = new NoMethodData(config, config.dataLayoutNoTag, TriState.UNKNOWN);
final long metaspaceMethodData;
private final HotSpotResolvedJavaMethodImpl method;
HotSpotMethodData(long metaspaceMethodData, HotSpotResolvedJavaMethodImpl method) {
this.metaspaceMethodData = metaspaceMethodData;
this.method = method;
}
private int normalDataSize() {
return UNSAFE.getInt(metaspaceMethodData + config.methodDataDataSize);
}
private int () {
final int extraDataBase = config.methodDataOopDataOffset + normalDataSize();
final int extraDataLimit = UNSAFE.getInt(metaspaceMethodData + config.methodDataSize);
return extraDataLimit - extraDataBase;
}
public boolean hasNormalData() {
return normalDataSize() > 0;
}
public boolean () {
return extraDataSize() > 0;
}
public int () {
return normalDataSize();
}
public boolean isWithin(int position) {
return position >= 0 && position < normalDataSize() + extraDataSize();
}
public int getDeoptimizationCount(DeoptimizationReason reason) {
HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess();
int reasonIndex = metaAccess.convertDeoptReason(reason);
return UNSAFE.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + reasonIndex) & 0xFF;
}
public int getOSRDeoptimizationCount(DeoptimizationReason reason) {
HotSpotMetaAccessProvider metaAccess = (HotSpotMetaAccessProvider) runtime().getHostJVMCIBackend().getMetaAccess();
int reasonIndex = metaAccess.convertDeoptReason(reason);
return UNSAFE.getByte(metaspaceMethodData + config.methodDataOopTrapHistoryOffset + config.deoptReasonOSROffset + reasonIndex) & 0xFF;
}
public int getDecompileCount() {
return UNSAFE.getInt(metaspaceMethodData + config.methodDataDecompiles);
}
public int getOverflowRecompileCount() {
return UNSAFE.getInt(metaspaceMethodData + config.methodDataOverflowRecompiles);
}
public int getOverflowTrapCount() {
return UNSAFE.getInt(metaspaceMethodData + config.methodDataOverflowTraps);
}
public HotSpotMethodDataAccessor getNormalData(int position) {
if (position >= normalDataSize()) {
return null;
}
return getData(position);
}
public HotSpotMethodDataAccessor (int position) {
if (position >= normalDataSize() + extraDataSize()) {
return null;
}
HotSpotMethodDataAccessor data = getData(position);
if (data != null) {
return data;
}
return data;
}
public static HotSpotMethodDataAccessor getNoDataAccessor(boolean exceptionPossiblyNotRecorded) {
if (exceptionPossiblyNotRecorded) {
return NO_DATA_EXCEPTION_POSSIBLY_NOT_RECORDED_ACCESSOR;
} else {
return NO_DATA_NO_EXCEPTION_ACCESSOR;
}
}
private HotSpotMethodDataAccessor getData(int position) {
assert position >= 0 : "out of bounds";
final int tag = HotSpotMethodDataAccessor.readTag(config, this, position);
HotSpotMethodDataAccessor accessor = PROFILE_DATA_ACCESSORS[tag];
assert accessor == null || accessor.getTag() == tag : "wrong data accessor " + accessor + " for tag " + tag;
return accessor;
}
int readUnsignedByte(int position, int offsetInBytes) {
long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
return UNSAFE.getByte(metaspaceMethodData + fullOffsetInBytes) & 0xFF;
}
int readUnsignedShort(int position, int offsetInBytes) {
long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
return UNSAFE.getShort(metaspaceMethodData + fullOffsetInBytes) & 0xFFFF;
}
private long readUnsignedInt(int position, int offsetInBytes) {
long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
return UNSAFE.getAddress(metaspaceMethodData + fullOffsetInBytes) & 0xFFFFFFFFL;
}
private int readUnsignedIntAsSignedInt(int position, int offsetInBytes) {
long value = readUnsignedInt(position, offsetInBytes);
return truncateLongToInt(value);
}
private int readInt(int position, int offsetInBytes) {
long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
return (int) UNSAFE.getAddress(metaspaceMethodData + fullOffsetInBytes);
}
private HotSpotResolvedJavaMethod readMethod(int position, int offsetInBytes) {
long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
return compilerToVM().getResolvedJavaMethod(null, metaspaceMethodData + fullOffsetInBytes);
}
private HotSpotResolvedObjectTypeImpl readKlass(int position, int offsetInBytes) {
long fullOffsetInBytes = computeFullOffset(position, offsetInBytes);
return compilerToVM().getResolvedJavaType(null, metaspaceMethodData + fullOffsetInBytes, false);
}
private static int truncateLongToInt(long value) {
return value > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) value;
}
private static int computeFullOffset(int position, int offsetInBytes) {
return config.methodDataOopDataOffset + position + offsetInBytes;
}
private static int cellIndexToOffset(int cells) {
return config.dataLayoutHeaderSize + cellsToBytes(cells);
}
private static int cellsToBytes(int cells) {
return cells * config.dataLayoutCellSize;
}
public boolean isProfileMature() {
return runtime().getCompilerToVM().isMature(metaspaceMethodData);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
String nl = String.format("%n");
String nlIndent = String.format("%n%38s", "");
sb.append("Raw method data for ");
sb.append(method.format("%H.%n(%p)"));
sb.append(":");
sb.append(nl);
sb.append(String.format("nof_decompiles(%d) nof_overflow_recompiles(%d) nof_overflow_traps(%d)%n",
getDecompileCount(), getOverflowRecompileCount(), getOverflowTrapCount()));
if (hasNormalData()) {
int pos = 0;
HotSpotMethodDataAccessor data;
while ((data = getNormalData(pos)) != null) {
if (pos != 0) {
sb.append(nl);
}
int bci = data.getBCI(this, pos);
sb.append(String.format("%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName()));
sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent));
pos = pos + data.getSize(this, pos);
}
}
if (hasExtraData()) {
int pos = getExtraDataBeginOffset();
HotSpotMethodDataAccessor data;
while ((data = getExtraData(pos)) != null) {
if (pos == getExtraDataBeginOffset()) {
sb.append(nl).append("--- Extra data:");
}
int bci = data.getBCI(this, pos);
sb.append(String.format("%n%-6d bci: %-6d%-20s", pos, bci, data.getClass().getSimpleName()));
sb.append(data.appendTo(new StringBuilder(), this, pos).toString().replace(nl, nlIndent));
pos = pos + data.getSize(this, pos);
}
}
return sb.toString();
}
static final int NO_DATA_SIZE = cellIndexToOffset(0);
static class NoMethodData extends HotSpotMethodDataAccessor {
private final TriState exceptionSeen;
protected NoMethodData(HotSpotVMConfig config, int tag, TriState exceptionSeen) {
super(config, tag, NO_DATA_SIZE);
this.exceptionSeen = exceptionSeen;
}
@Override
public int getBCI(HotSpotMethodData data, int position) {
return -1;
}
@Override
public TriState getExceptionSeen(HotSpotMethodData data, int position) {
return exceptionSeen;
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
return sb;
}
}
static final int BIT_DATA_SIZE = cellIndexToOffset(0);
static final int BIT_DATA_NULL_SEEN_FLAG = 1 << config.bitDataNullSeenFlag;
static class BitData extends HotSpotMethodDataAccessor {
private BitData(HotSpotVMConfig config, int tag) {
super(config, tag, BIT_DATA_SIZE);
}
protected BitData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
public TriState getNullSeen(HotSpotMethodData data, int position) {
return TriState.get((getFlags(data, position) & BIT_DATA_NULL_SEEN_FLAG) != 0);
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
return sb.append(format("exception_seen(%s)", getExceptionSeen(data, pos)));
}
}
static final int COUNTER_DATA_SIZE = cellIndexToOffset(1);
static final int COUNTER_DATA_COUNT_OFFSET = cellIndexToOffset(config.methodDataCountOffset);
static class CounterData extends BitData {
CounterData(HotSpotVMConfig config, int tag) {
super(config, tag, COUNTER_DATA_SIZE);
}
protected CounterData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
public int getExecutionCount(HotSpotMethodData data, int position) {
return getCounterValue(data, position);
}
protected int getCounterValue(HotSpotMethodData data, int position) {
return data.readUnsignedIntAsSignedInt(position, COUNTER_DATA_COUNT_OFFSET);
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos)));
}
}
static final int JUMP_DATA_SIZE = cellIndexToOffset(2);
static final int TAKEN_COUNT_OFFSET = cellIndexToOffset(config.jumpDataTakenOffset);
static final int TAKEN_DISPLACEMENT_OFFSET = cellIndexToOffset(config.jumpDataDisplacementOffset);
static class JumpData extends HotSpotMethodDataAccessor {
JumpData(HotSpotVMConfig config, int tag) {
super(config, tag, JUMP_DATA_SIZE);
}
protected JumpData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
public double getBranchTakenProbability(HotSpotMethodData data, int position) {
return getExecutionCount(data, position) != 0 ? 1 : 0;
}
@Override
public int getExecutionCount(HotSpotMethodData data, int position) {
return data.readUnsignedIntAsSignedInt(position, TAKEN_COUNT_OFFSET);
}
public int getTakenDisplacement(HotSpotMethodData data, int position) {
return data.readInt(position, TAKEN_DISPLACEMENT_OFFSET);
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
return sb.append(format("taken(%d) displacement(%d)", getExecutionCount(data, pos), getTakenDisplacement(data, pos)));
}
}
static class RawItemProfile<T> {
final int entries;
final T[] items;
final long[] counts;
final long totalCount;
RawItemProfile(int entries, T[] items, long[] counts, long totalCount) {
this.entries = entries;
this.items = items;
this.counts = counts;
this.totalCount = totalCount;
}
}
static final int TYPE_DATA_ROW_SIZE = cellsToBytes(config.receiverTypeDataReceiverTypeRowCellCount);
static final int NONPROFILED_COUNT_OFFSET = cellIndexToOffset(config.receiverTypeDataNonprofiledCountOffset);
static final int TYPE_DATA_FIRST_TYPE_OFFSET = cellIndexToOffset(config.receiverTypeDataReceiver0Offset);
static final int TYPE_DATA_FIRST_TYPE_COUNT_OFFSET = cellIndexToOffset(config.receiverTypeDataCount0Offset);
abstract static class AbstractTypeData extends CounterData {
protected AbstractTypeData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
public JavaTypeProfile getTypeProfile(HotSpotMethodData data, int position) {
return createTypeProfile(getNullSeen(data, position), getRawTypeProfile(data, position));
}
private RawItemProfile<ResolvedJavaType> getRawTypeProfile(HotSpotMethodData data, int position) {
int typeProfileWidth = config.typeProfileWidth;
ResolvedJavaType[] types = new ResolvedJavaType[typeProfileWidth];
long[] counts = new long[typeProfileWidth];
long totalCount = 0;
int entries = 0;
outer: for (int i = 0; i < typeProfileWidth; i++) {
HotSpotResolvedObjectTypeImpl receiverKlass = data.readKlass(position, getTypeOffset(i));
if (receiverKlass != null) {
HotSpotResolvedObjectTypeImpl klass = receiverKlass;
long count = data.readUnsignedInt(position, getTypeCountOffset(i));
for (int j = 0; j < entries; j++) {
if (types[j].equals(klass)) {
totalCount += count;
counts[j] += count;
continue outer;
}
}
types[entries] = klass;
totalCount += count;
counts[entries] = count;
entries++;
}
}
totalCount += getTypesNotRecordedExecutionCount(data, position);
return new RawItemProfile<>(entries, types, counts, totalCount);
}
protected abstract long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position);
public int getNonprofiledCount(HotSpotMethodData data, int position) {
return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET);
}
private JavaTypeProfile createTypeProfile(TriState nullSeen, RawItemProfile<ResolvedJavaType> profile) {
if (profile.entries <= 0 || profile.totalCount <= 0) {
return null;
}
ProfiledType[] ptypes = new ProfiledType[profile.entries];
double totalProbability = 0.0;
for (int i = 0; i < profile.entries; i++) {
double p = profile.counts[i];
p = p / profile.totalCount;
totalProbability += p;
ptypes[i] = new ProfiledType(profile.items[i], p);
}
Arrays.sort(ptypes);
double notRecordedTypeProbability = profile.entries < config.typeProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
assert notRecordedTypeProbability == 0 || profile.entries == config.typeProfileWidth;
return new JavaTypeProfile(nullSeen, notRecordedTypeProbability, ptypes);
}
private static int getTypeOffset(int row) {
return TYPE_DATA_FIRST_TYPE_OFFSET + row * TYPE_DATA_ROW_SIZE;
}
protected static int getTypeCountOffset(int row) {
return TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE;
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos);
TriState nullSeen = getNullSeen(data, pos);
TriState exceptionSeen = getExceptionSeen(data, pos);
sb.append(format("count(%d) null_seen(%s) exception_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen,
getNonprofiledCount(data, pos), profile.entries));
for (int i = 0; i < profile.entries; i++) {
long count = profile.counts[i];
sb.append(format("%n %s (%d, %4.2f)", profile.items[i].toJavaName(), count, (double) count / profile.totalCount));
}
return sb;
}
}
static final int TYPE_CHECK_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
static class ReceiverTypeData extends AbstractTypeData {
ReceiverTypeData(HotSpotVMConfig config, int tag) {
super(config, tag, TYPE_CHECK_DATA_SIZE);
}
protected ReceiverTypeData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
public int getExecutionCount(HotSpotMethodData data, int position) {
return -1;
}
@Override
protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
return getNonprofiledCount(data, position);
}
}
static final int VIRTUAL_CALL_DATA_SIZE = cellIndexToOffset(2) + TYPE_DATA_ROW_SIZE * (config.typeProfileWidth + config.methodProfileWidth);
static final int VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET = TYPE_DATA_FIRST_TYPE_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
static final int VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET = TYPE_DATA_FIRST_TYPE_COUNT_OFFSET + TYPE_DATA_ROW_SIZE * config.typeProfileWidth;
static class VirtualCallData extends ReceiverTypeData {
VirtualCallData(HotSpotVMConfig config, int tag) {
super(config, tag, VIRTUAL_CALL_DATA_SIZE);
}
protected VirtualCallData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
public int getExecutionCount(HotSpotMethodData data, int position) {
final int typeProfileWidth = config.typeProfileWidth;
long total = 0;
for (int i = 0; i < typeProfileWidth; i++) {
total += data.readUnsignedInt(position, getTypeCountOffset(i));
}
total += getCounterValue(data, position);
return truncateLongToInt(total);
}
@Override
protected long getTypesNotRecordedExecutionCount(HotSpotMethodData data, int position) {
return getCounterValue(data, position);
}
private static long getMethodsNotRecordedExecutionCount(HotSpotMethodData data, int position) {
return data.readUnsignedIntAsSignedInt(position, NONPROFILED_COUNT_OFFSET);
}
@Override
public JavaMethodProfile getMethodProfile(HotSpotMethodData data, int position) {
return createMethodProfile(getRawMethodProfile(data, position));
}
private RawItemProfile<ResolvedJavaMethod> getRawMethodProfile(HotSpotMethodData data, int position) {
int profileWidth = config.methodProfileWidth;
ResolvedJavaMethod[] methods = new ResolvedJavaMethod[profileWidth];
long[] counts = new long[profileWidth];
long totalCount = 0;
int entries = 0;
for (int i = 0; i < profileWidth; i++) {
HotSpotResolvedJavaMethod method = data.readMethod(position, getMethodOffset(i));
if (method != null) {
methods[entries] = method;
long count = data.readUnsignedInt(position, getMethodCountOffset(i));
totalCount += count;
counts[entries] = count;
entries++;
}
}
totalCount += getMethodsNotRecordedExecutionCount(data, position);
return new RawItemProfile<>(entries, methods, counts, totalCount);
}
private JavaMethodProfile createMethodProfile(RawItemProfile<ResolvedJavaMethod> profile) {
if (profile.entries <= 0 || profile.totalCount <= 0) {
return null;
}
ProfiledMethod[] pmethods = new ProfiledMethod[profile.entries];
double totalProbability = 0.0;
for (int i = 0; i < profile.entries; i++) {
double p = profile.counts[i];
p = p / profile.totalCount;
totalProbability += p;
pmethods[i] = new ProfiledMethod(profile.items[i], p);
}
Arrays.sort(pmethods);
double notRecordedMethodProbability = profile.entries < config.methodProfileWidth ? 0.0 : Math.min(1.0, Math.max(0.0, 1.0 - totalProbability));
assert notRecordedMethodProbability == 0 || profile.entries == config.methodProfileWidth;
return new JavaMethodProfile(notRecordedMethodProbability, pmethods);
}
private static int getMethodOffset(int row) {
return VIRTUAL_CALL_DATA_FIRST_METHOD_OFFSET + row * TYPE_DATA_ROW_SIZE;
}
private static int getMethodCountOffset(int row) {
return VIRTUAL_CALL_DATA_FIRST_METHOD_COUNT_OFFSET + row * TYPE_DATA_ROW_SIZE;
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
RawItemProfile<ResolvedJavaMethod> profile = getRawMethodProfile(data, pos);
super.appendTo(sb.append(format("exception_seen(%s) ", getExceptionSeen(data, pos))), data, pos).append(format("%nmethod_entries(%d)", profile.entries));
for (int i = 0; i < profile.entries; i++) {
long count = profile.counts[i];
sb.append(format("%n %s (%d, %4.2f)", profile.items[i].format("%H.%n(%p)"), count, (double) count / profile.totalCount));
}
return sb;
}
}
static class VirtualCallTypeData extends VirtualCallData {
VirtualCallTypeData(HotSpotVMConfig config, int tag) {
super(config, tag, 0);
}
@Override
protected int getDynamicSize(HotSpotMethodData data, int position) {
assert staticSize == 0;
return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.metaspaceMethodData, position);
}
}
static final int RET_DATA_ROW_SIZE = cellsToBytes(3);
static final int RET_DATA_SIZE = cellIndexToOffset(1) + RET_DATA_ROW_SIZE * config.bciProfileWidth;
static class RetData extends CounterData {
RetData(HotSpotVMConfig config, int tag) {
super(config, tag, RET_DATA_SIZE);
}
}
static final int BRANCH_DATA_SIZE = cellIndexToOffset(3);
static final int NOT_TAKEN_COUNT_OFFSET = cellIndexToOffset(config.branchDataNotTakenOffset);
static class BranchData extends JumpData {
BranchData(HotSpotVMConfig config, int tag) {
super(config, tag, BRANCH_DATA_SIZE);
}
@Override
public double getBranchTakenProbability(HotSpotMethodData data, int position) {
long takenCount = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET);
long notTakenCount = data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET);
long total = takenCount + notTakenCount;
return total <= 0 ? -1 : takenCount / (double) total;
}
@Override
public int getExecutionCount(HotSpotMethodData data, int position) {
long count = data.readUnsignedInt(position, TAKEN_COUNT_OFFSET) + data.readUnsignedInt(position, NOT_TAKEN_COUNT_OFFSET);
return truncateLongToInt(count);
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
long taken = data.readUnsignedInt(pos, TAKEN_COUNT_OFFSET);
long notTaken = data.readUnsignedInt(pos, NOT_TAKEN_COUNT_OFFSET);
double takenProbability = getBranchTakenProbability(data, pos);
return sb.append(format("taken(%d, %4.2f) not_taken(%d, %4.2f) displacement(%d)", taken, takenProbability, notTaken, 1.0D - takenProbability, getTakenDisplacement(data, pos)));
}
}
static final int ARRAY_DATA_LENGTH_OFFSET = cellIndexToOffset(config.arrayDataArrayLenOffset);
static final int ARRAY_DATA_START_OFFSET = cellIndexToOffset(config.arrayDataArrayStartOffset);
static class ArrayData extends HotSpotMethodDataAccessor {
ArrayData(HotSpotVMConfig config, int tag, int staticSize) {
super(config, tag, staticSize);
}
@Override
protected int getDynamicSize(HotSpotMethodData data, int position) {
return cellsToBytes(getLength(data, position));
}
protected static int getLength(HotSpotMethodData data, int position) {
return data.readInt(position, ARRAY_DATA_LENGTH_OFFSET);
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
return sb.append(format("length(%d)", getLength(data, pos)));
}
}
static final int MULTI_BRANCH_DATA_SIZE = cellIndexToOffset(1);
static final int MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS = config.multiBranchDataPerCaseCellCount;
static final int MULTI_BRANCH_DATA_ROW_SIZE = cellsToBytes(MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS);
static final int MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(0);
static final int MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET = ARRAY_DATA_START_OFFSET + cellsToBytes(1);
static class MultiBranchData extends ArrayData {
MultiBranchData(HotSpotVMConfig config, int tag) {
super(config, tag, MULTI_BRANCH_DATA_SIZE);
}
@Override
public double[] getSwitchProbabilities(HotSpotMethodData data, int position) {
int arrayLength = getLength(data, position);
assert arrayLength > 0 : "switch must have at least the default case";
assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows";
int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
long totalCount = 0;
double[] result = new double[length];
long count = readCount(data, position, 0);
totalCount += count;
result[length - 1] = count;
for (int i = 1; i < length; i++) {
count = readCount(data, position, i);
totalCount += count;
result[i - 1] = count;
}
if (totalCount <= 0) {
return null;
} else {
for (int i = 0; i < length; i++) {
result[i] = result[i] / totalCount;
}
return result;
}
}
private static long readCount(HotSpotMethodData data, int position, int i) {
int offset;
long count;
offset = getCountOffset(i);
count = data.readUnsignedInt(position, offset);
return count;
}
@Override
public int getExecutionCount(HotSpotMethodData data, int position) {
int arrayLength = getLength(data, position);
assert arrayLength > 0 : "switch must have at least the default case";
assert arrayLength % MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS == 0 : "array must have full rows";
int length = arrayLength / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
long totalCount = 0;
for (int i = 0; i < length; i++) {
int offset = getCountOffset(i);
totalCount += data.readUnsignedInt(position, offset);
}
return truncateLongToInt(totalCount);
}
private static int getCountOffset(int index) {
return MULTI_BRANCH_DATA_FIRST_COUNT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE;
}
private static int getDisplacementOffset(int index) {
return MULTI_BRANCH_DATA_FIRST_DISPLACEMENT_OFFSET + index * MULTI_BRANCH_DATA_ROW_SIZE;
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
int entries = getLength(data, pos) / MULTI_BRANCH_DATA_ROW_SIZE_IN_CELLS;
sb.append(format("entries(%d)", entries));
for (int i = 0; i < entries; i++) {
sb.append(format("%n %d: count(%d) displacement(%d)", i, data.readUnsignedInt(pos, getCountOffset(i)), data.readUnsignedInt(pos, getDisplacementOffset(i))));
}
return sb;
}
}
static final int ARG_INFO_DATA_SIZE = cellIndexToOffset(1);
static class ArgInfoData extends ArrayData {
ArgInfoData(HotSpotVMConfig config, int tag) {
super(config, tag, ARG_INFO_DATA_SIZE);
}
}
static class UnknownProfileData extends HotSpotMethodDataAccessor {
UnknownProfileData(HotSpotVMConfig config, int tag) {
super(config, tag, 0);
}
@Override
protected int getDynamicSize(HotSpotMethodData data, int position) {
assert staticSize == 0;
return HotSpotJVMCIRuntime.runtime().compilerToVm.methodDataProfileDataSize(data.metaspaceMethodData, position);
}
@Override
public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
sb.append("unknown profile data with tag: " + tag);
return sb;
}
}
public void setCompiledIRSize(int size) {
UNSAFE.putInt(metaspaceMethodData + config.methodDataIRSizeOffset, size);
}
public int getCompiledIRSize() {
return UNSAFE.getInt(metaspaceMethodData + config.methodDataIRSizeOffset);
}
static final HotSpotMethodDataAccessor[] PROFILE_DATA_ACCESSORS = {
null,
new BitData(config, config.dataLayoutBitDataTag),
new CounterData(config, config.dataLayoutCounterDataTag),
new JumpData(config, config.dataLayoutJumpDataTag),
new ReceiverTypeData(config, config.dataLayoutReceiverTypeDataTag),
new VirtualCallData(config, config.dataLayoutVirtualCallDataTag),
new RetData(config, config.dataLayoutRetDataTag),
new BranchData(config, config.dataLayoutBranchDataTag),
new MultiBranchData(config, config.dataLayoutMultiBranchDataTag),
new ArgInfoData(config, config.dataLayoutArgInfoDataTag),
new UnknownProfileData(config, config.dataLayoutCallTypeDataTag),
new VirtualCallTypeData(config, config.dataLayoutVirtualCallTypeDataTag),
new UnknownProfileData(config, config.dataLayoutParametersTypeDataTag),
new UnknownProfileData(config, config.dataLayoutSpeculativeTrapDataTag),
};
private static boolean checkAccessorTags() {
int expectedTag = 0;
for (HotSpotMethodDataAccessor accessor : PROFILE_DATA_ACCESSORS) {
if (expectedTag == 0) {
assert accessor == null;
} else {
assert accessor.tag == expectedTag : expectedTag + " != " + accessor.tag + " " + accessor;
}
expectedTag++;
}
return true;
}
static {
assert checkAccessorTags();
}
}