package com.oracle.svm.core.code;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.function.CodePointer;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.RuntimeAssertionsSupport;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.NeverInline;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.NonmovableArray;
import com.oracle.svm.core.c.NonmovableArrays;
import com.oracle.svm.core.c.NonmovableObjectArray;
import com.oracle.svm.core.code.FrameInfoDecoder.ValueInfoAllocator;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.thread.VMOperation;
public final class CodeInfoAccess {
private CodeInfoAccess() {
}
@Fold
static boolean haveAssertions() {
return RuntimeAssertionsSupport.singleton().desiredAssertionStatus(CodeInfoAccess.class);
}
@Uninterruptible(reason = "The handle should only be accessed from uninterruptible code to prevent that the GC frees the CodeInfo.", callerMustBe = true)
public static Object acquireTether(UntetheredCodeInfo info) {
Object tether = UntetheredCodeInfoAccess.getTetherUnsafe(info);
assert VMOperation.isGCInProgress() || ((CodeInfoTether) tether).incrementCount() > 0;
return tether;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
@NeverInline("Prevent elimination of object reference in caller.")
public static void releaseTether(UntetheredCodeInfo info, Object tether) {
assert VMOperation.isGCInProgress() || UntetheredCodeInfoAccess.getTetherUnsafe(info) == null || UntetheredCodeInfoAccess.getTetherUnsafe(info) == tether;
assert VMOperation.isGCInProgress() || ((CodeInfoTether) tether).decrementCount() >= 0;
}
@Uninterruptible(reason = "Called during teardown.", callerMustBe = true)
@NeverInline("Prevent elimination of object reference in caller.")
public static void releaseTetherUnsafe(@SuppressWarnings("unused") UntetheredCodeInfo info, Object tether) {
assert VMOperation.isGCInProgress() || ((CodeInfoTether) tether).decrementCount() >= 0;
}
@Uninterruptible(reason = "Should be called from the same method as acquireTether.", callerMustBe = true)
public static CodeInfo convert(UntetheredCodeInfo untetheredInfo, Object tether) {
assert UntetheredCodeInfoAccess.getTetherUnsafe(untetheredInfo) == null || UntetheredCodeInfoAccess.getTetherUnsafe(untetheredInfo) == tether;
return convert(untetheredInfo);
}
@Uninterruptible(reason = "Called by uninterruptible code.", mayBeInlined = true)
public static CodeInfo convert(UntetheredCodeInfo untetheredInfo) {
assert isValid(untetheredInfo);
return (CodeInfo) untetheredInfo;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isValid(UntetheredCodeInfo info) {
return SubstrateUtil.HOSTED || !haveAssertions() || VMOperation.isGCInProgress() || UntetheredCodeInfoAccess.getTetherUnsafe(info) == null ||
((CodeInfoTether) UntetheredCodeInfoAccess.getTetherUnsafe(info)).getCount() > 0;
}
@Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true)
public static void setState(CodeInfo info, int state) {
assert getState(info) < state;
cast(info).setState(state);
}
@Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true)
public static int getState(CodeInfo info) {
return cast(info).getState();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isAlive(CodeInfo info) {
return isAliveState(cast(info).getState());
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isAliveState(int state) {
return state == CodeInfo.STATE_CODE_CONSTANTS_LIVE || state == CodeInfo.STATE_NON_ENTRANT;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static CodePointer getCodeStart(CodeInfo info) {
return cast(info).getCodeStart();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getCodeSize(CodeInfo info) {
return cast(info).getCodeSize();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getDataSize(CodeInfo info) {
return cast(info).getDataSize();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getDataOffset(CodeInfo info) {
return cast(info).getDataOffset();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getCodeAndDataMemorySize(CodeInfo info) {
return cast(info).getCodeAndDataMemorySize();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static UnsignedWord getNativeMetadataSize(CodeInfo info) {
CodeInfoImpl impl = cast(info);
UnsignedWord size = SizeOf.unsigned(CodeInfo.class);
if (!impl.getAllObjectsAreInImageHeap()) {
size = size.add(NonmovableArrays.byteSizeOf(impl.getObjectFields()))
.add(NonmovableArrays.byteSizeOf(impl.getCodeInfoIndex()))
.add(NonmovableArrays.byteSizeOf(impl.getCodeInfoEncodings()))
.add(NonmovableArrays.byteSizeOf(impl.getStackReferenceMapEncoding()))
.add(NonmovableArrays.byteSizeOf(impl.getFrameInfoEncodings()))
.add(NonmovableArrays.byteSizeOf(impl.getFrameInfoObjectConstants()))
.add(NonmovableArrays.byteSizeOf(impl.getFrameInfoSourceClasses()))
.add(NonmovableArrays.byteSizeOf(impl.getFrameInfoSourceMethodNames()))
.add(NonmovableArrays.byteSizeOf(impl.getFrameInfoNames()))
.add(NonmovableArrays.byteSizeOf(impl.getDeoptimizationStartOffsets()))
.add(NonmovableArrays.byteSizeOf(impl.getDeoptimizationEncodings()))
.add(NonmovableArrays.byteSizeOf(impl.getDeoptimizationObjectConstants()))
.add(NonmovableArrays.byteSizeOf(impl.getCodeConstantsReferenceMapEncoding()));
}
return size;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean contains(CodeInfo info, CodePointer ip) {
CodeInfoImpl impl = cast(info);
return ((UnsignedWord) ip).subtract((UnsignedWord) impl.getCodeStart()).belowThan(impl.getCodeSize());
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static long relativeIP(CodeInfo info, CodePointer ip) {
assert contains(info, ip);
return ((UnsignedWord) ip).subtract((UnsignedWord) cast(info).getCodeStart()).rawValue();
}
public static CodePointer absoluteIP(CodeInfo info, long relativeIP) {
return (CodePointer) ((UnsignedWord) cast(info).getCodeStart()).add(WordFactory.unsigned(relativeIP));
}
public static long initFrameInfoReader(CodeInfo info, CodePointer ip, ReusableTypeReader frameInfoReader) {
long entryOffset = CodeInfoDecoder.lookupCodeInfoEntryOffset(info, relativeIP(info, ip));
if (entryOffset >= 0) {
if (!CodeInfoDecoder.initFrameInfoReader(info, entryOffset, frameInfoReader)) {
return -1;
}
}
return entryOffset;
}
public static FrameInfoQueryResult nextFrameInfo(CodeInfo info, long entryOffset, ReusableTypeReader frameInfoReader,
FrameInfoDecoder.FrameInfoQueryResultAllocator resultAllocator, ValueInfoAllocator valueInfoAllocator, boolean fetchFirstFrame) {
int entryFlags = CodeInfoDecoder.loadEntryFlags(info, entryOffset);
boolean isDeoptEntry = CodeInfoDecoder.extractFI(entryFlags) == CodeInfoDecoder.FI_DEOPT_ENTRY_INDEX_S4;
return FrameInfoDecoder.decodeFrameInfo(isDeoptEntry, frameInfoReader, info, resultAllocator, valueInfoAllocator, fetchFirstFrame);
}
@SuppressWarnings("unchecked")
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
static <T> T getObjectField(CodeInfo info, int index) {
return (T) NonmovableArrays.getObject(cast(info).getObjectFields(), index);
}
public static String getName(CodeInfo info) {
return getObjectField(info, CodeInfoImpl.NAME_OBJFIELD);
}
public static long lookupDeoptimizationEntrypoint(CodeInfo info, long method, long encodedBci, CodeInfoQueryResult codeInfo) {
return CodeInfoDecoder.lookupDeoptimizationEntrypoint(info, method, encodedBci, codeInfo);
}
public static long lookupTotalFrameSize(CodeInfo info, long ip) {
SimpleCodeInfoQueryResult codeInfoQueryResult = StackValue.get(SimpleCodeInfoQueryResult.class);
lookupCodeInfo(info, ip, codeInfoQueryResult);
return CodeInfoQueryResult.getTotalFrameSize(codeInfoQueryResult.getEncodedFrameSize());
}
public static NonmovableArray<Byte> getStackReferenceMapEncoding(CodeInfo info) {
return cast(info).getStackReferenceMapEncoding();
}
public static long lookupStackReferenceMapIndex(CodeInfo info, long ip) {
return CodeInfoDecoder.lookupStackReferenceMapIndex(info, ip);
}
public static void lookupCodeInfo(CodeInfo info, long ip, CodeInfoQueryResult codeInfoQueryResult) {
CodeInfoDecoder.lookupCodeInfo(info, ip, codeInfoQueryResult);
}
public static void lookupCodeInfo(CodeInfo info, long ip, SimpleCodeInfoQueryResult codeInfoQueryResult) {
CodeInfoDecoder.lookupCodeInfo(info, ip, codeInfoQueryResult);
}
@Uninterruptible(reason = "Nonmovable object arrays are not visible to GC until installed.")
public static void setFrameInfo(CodeInfo info, NonmovableArray<Byte> encodings, NonmovableObjectArray<Object> objectConstants,
NonmovableObjectArray<Class<?>> sourceClasses, NonmovableObjectArray<String> sourceMethodNames, NonmovableObjectArray<String> names) {
CodeInfoImpl impl = cast(info);
impl.setFrameInfoEncodings(encodings);
impl.setFrameInfoObjectConstants(objectConstants);
impl.setFrameInfoSourceClasses(sourceClasses);
impl.setFrameInfoSourceMethodNames(sourceMethodNames);
impl.setFrameInfoNames(names);
if (!SubstrateUtil.HOSTED) {
Heap.getHeap().getRuntimeCodeInfoGCSupport().registerFrameMetadata(impl);
}
}
public static void setCodeInfo(CodeInfo info, NonmovableArray<Byte> index, NonmovableArray<Byte> encodings, NonmovableArray<Byte> referenceMapEncoding) {
CodeInfoImpl impl = cast(info);
impl.setCodeInfoIndex(index);
impl.setCodeInfoEncodings(encodings);
impl.setStackReferenceMapEncoding(referenceMapEncoding);
}
public static Log log(CodeInfo info, Log log) {
return info.isNull() ? log.string("null") : log.string(CodeInfo.class.getName()).string("@").hex(info);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static int getTier(CodeInfo info) {
return cast(info).getTier();
}
static NonmovableArray<Integer> getDeoptimizationStartOffsets(CodeInfo info) {
return cast(info).getDeoptimizationStartOffsets();
}
static NonmovableArray<Byte> getDeoptimizationEncodings(CodeInfo info) {
return cast(info).getDeoptimizationEncodings();
}
static NonmovableObjectArray<Object> getDeoptimizationObjectConstants(CodeInfo info) {
return cast(info).getDeoptimizationObjectConstants();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
static CodePointer getCodeEnd(CodeInfo info) {
CodeInfoImpl impl = cast(info);
return (CodePointer) ((UnsignedWord) impl.getCodeStart()).add(impl.getCodeSize());
}
public static NonmovableArray<Byte> getCodeInfoIndex(CodeInfo info) {
return cast(info).getCodeInfoIndex();
}
public static NonmovableArray<Byte> getCodeInfoEncodings(CodeInfo info) {
return cast(info).getCodeInfoEncodings();
}
public static NonmovableArray<Byte> getFrameInfoEncodings(CodeInfo info) {
return cast(info).getFrameInfoEncodings();
}
public static NonmovableObjectArray<Object> getFrameInfoObjectConstants(CodeInfo info) {
return cast(info).getFrameInfoObjectConstants();
}
public static NonmovableObjectArray<Class<?>> getFrameInfoSourceClasses(CodeInfo info) {
return cast(info).getFrameInfoSourceClasses();
}
public static NonmovableObjectArray<String> getFrameInfoSourceMethodNames(CodeInfo info) {
return cast(info).getFrameInfoSourceMethodNames();
}
public static NonmovableObjectArray<String> getFrameInfoNames(CodeInfo info) {
return cast(info).getFrameInfoNames();
}
@Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true)
private static CodeInfoImpl cast(UntetheredCodeInfo info) {
assert isValid(info);
return (CodeInfoImpl) info;
}
}