From 213b71cd2efe398ec8f6dc4c4318a38f8d9fd7cf Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Tue, 28 Sep 2021 22:56:59 +0200 Subject: [PATCH] Add compressed frame encoding scheme. --- .../com/oracle/svm/core/SubstrateOptions.java | 3 + .../oracle/svm/core/code/CodeInfoAccess.java | 40 ++- .../oracle/svm/core/code/CodeInfoDecoder.java | 4 +- .../oracle/svm/core/code/CodeInfoEncoder.java | 26 +- .../oracle/svm/core/code/CodeInfoImpl.java | 6 - .../oracle/svm/core/code/CodeInfoOffsets.java | 4 - .../oracle/svm/core/code/CodeInfoTable.java | 1 - .../svm/core/code/FrameInfoDecoder.java | 179 +++++++--- .../svm/core/code/FrameInfoEncoder.java | 323 +++++++++++++----- .../svm/core/code/FrameInfoQueryResult.java | 19 +- .../oracle/svm/core/code/ImageCodeInfo.java | 12 - .../svm/core/code/RuntimeCodeInfoAccess.java | 2 - .../oracle/svm/core/deopt/Deoptimizer.java | 4 - .../stack/SubstrateStackIntrospection.java | 4 - .../svm/core/stack/ThreadStackPrinter.java | 8 +- .../svm/graal/meta/RuntimeCodeInstaller.java | 20 +- .../hosted/image/NativeImageCodeCache.java | 8 +- 17 files changed, 439 insertions(+), 224 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index c4e350b410ac..43e972075f0d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -354,6 +354,9 @@ public static final long getTearDownFailureNanos() { @Option(help = "Use callee saved registers to reduce spilling for low-frequency calls to stubs (if callee saved registers are supported by the architecture)")// public static final HostedOptionKey UseCalleeSavedRegisters = new HostedOptionKey<>(true); + @Option(help = "Use compressed frame encoding for frames without local values.", type = OptionType.Expert)// + public static final HostedOptionKey UseCompressedFrameEncodings = new HostedOptionKey<>(true); + @Option(help = "Report error if [:{,}] is discovered during analysis (valid values for UsageKind: InHeap, Allocated, Reachable).", type = OptionType.Debug)// public static final HostedOptionKey ReportAnalysisForbiddenType = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java index 27be5a52b763..0ac344d0be9e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoAccess.java @@ -214,7 +214,6 @@ public static UnsignedWord getNativeMetadataSize(CodeInfo info) { .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())) @@ -239,21 +238,39 @@ 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) { + public static class FrameInfoState { + public long entryOffset; + public boolean isFirstFrame; + public boolean isDone; + public int firstValue; + + public FrameInfoState() { + reset(); + } + + public void reset() { + entryOffset = -1; + isFirstFrame = true; + isDone = false; + firstValue = -1; + } + } + + public static void initFrameInfoReader(CodeInfo info, CodePointer ip, ReusableTypeReader frameInfoReader, FrameInfoState state) { long entryOffset = CodeInfoDecoder.lookupCodeInfoEntryOffset(info, relativeIP(info, ip)); + state.entryOffset = entryOffset; if (entryOffset >= 0) { if (!CodeInfoDecoder.initFrameInfoReader(info, entryOffset, frameInfoReader)) { - return -1; + state.entryOffset = -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); + public static FrameInfoQueryResult nextFrameInfo(CodeInfo info, ReusableTypeReader frameInfoReader, + FrameInfoDecoder.FrameInfoQueryResultAllocator resultAllocator, ValueInfoAllocator valueInfoAllocator, FrameInfoState state) { + int entryFlags = CodeInfoDecoder.loadEntryFlags(info, state.entryOffset); boolean isDeoptEntry = CodeInfoDecoder.extractFI(entryFlags) == CodeInfoDecoder.FI_DEOPT_ENTRY_INDEX_S4; - return FrameInfoDecoder.decodeFrameInfo(isDeoptEntry, frameInfoReader, info, resultAllocator, valueInfoAllocator, fetchFirstFrame); + return FrameInfoDecoder.decodeFrameInfo(isDeoptEntry, frameInfoReader, info, resultAllocator, valueInfoAllocator, state); } @SuppressWarnings("unchecked") @@ -307,12 +324,11 @@ public static void setCodeInfo(CodeInfo info, NonmovableArray index, Nonmo @Uninterruptible(reason = "Nonmovable object arrays are not visible to GC until installed.") public static void setEncodings(CodeInfo info, NonmovableObjectArray objectConstants, - NonmovableObjectArray> sourceClasses, NonmovableObjectArray sourceMethodNames, NonmovableObjectArray names) { + NonmovableObjectArray> sourceClasses, NonmovableObjectArray sourceMethodNames) { CodeInfoImpl impl = cast(info); impl.setFrameInfoObjectConstants(objectConstants); impl.setFrameInfoSourceClasses(sourceClasses); impl.setFrameInfoSourceMethodNames(sourceMethodNames); - impl.setFrameInfoNames(names); if (!SubstrateUtil.HOSTED) { // notify the GC about the frame metadata that is now live Heap.getHeap().getRuntimeCodeInfoGCSupport().registerFrameMetadata(impl); @@ -370,10 +386,6 @@ public static NonmovableObjectArray getFrameInfoSourceMethodNames(CodeIn return cast(info).getFrameInfoSourceMethodNames(); } - public static NonmovableObjectArray getFrameInfoNames(CodeInfo info) { - return cast(info).getFrameInfoNames(); - } - @Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true) private static CodeInfoImpl cast(UntetheredCodeInfo info) { assert isValid(info); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java index d1cfdb170f44..3f511c8a6d40 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java @@ -29,8 +29,8 @@ // Checkstyle: stop import java.lang.reflect.Executable; // Checkstyle: resume -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.graalvm.compiler.api.replacements.Fold; @@ -367,7 +367,7 @@ private static FrameInfoQueryResult loadFrameInfo(CodeInfo info, long entryOffse } int frameInfoIndex = NonmovableByteArrayReader.getS4(CodeInfoAccess.getCodeInfoEncodings(info), offsetFI(entryOffset, entryFlags)); return FrameInfoDecoder.decodeFrameInfo(isDeoptEntry, new ReusableTypeReader(CodeInfoAccess.getFrameInfoEncodings(info), frameInfoIndex), info, - FrameInfoDecoder.HeapBasedFrameInfoQueryResultAllocator, FrameInfoDecoder.HeapBasedValueInfoAllocator, true); + FrameInfoDecoder.HeapBasedFrameInfoQueryResultAllocator, FrameInfoDecoder.HeapBasedValueInfoAllocator); } @AlwaysInline("Make IP-lookup loop call free") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index f43f2fe72f06..910ff742cf13 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -116,7 +116,7 @@ private Encoders() { this.objectConstants = FrequencyEncoder.createEqualityEncoder(); this.sourceClasses = FrequencyEncoder.createEqualityEncoder(); this.sourceMethodNames = FrequencyEncoder.createEqualityEncoder(); - if (FrameInfoDecoder.encodeDebugNames() || FrameInfoDecoder.encodeSourceReferences()) { + if (FrameInfoDecoder.encodeSourceReferences()) { this.names = FrequencyEncoder.createEqualityEncoder(); } else { this.names = null; @@ -127,28 +127,22 @@ private void encodeAllAndInstall(CodeInfo target, ReferenceAdjuster adjuster) { JavaConstant[] encodedJavaConstants = objectConstants.encodeAll(new JavaConstant[objectConstants.getLength()]); Class[] sourceClassesArray = null; String[] sourceMethodNamesArray = null; - String[] namesArray = null; - final boolean encodeDebugNames = FrameInfoDecoder.encodeDebugNames(); - if (encodeDebugNames || FrameInfoDecoder.encodeSourceReferences()) { + if (FrameInfoDecoder.encodeSourceReferences()) { sourceClassesArray = sourceClasses.encodeAll(new Class[sourceClasses.getLength()]); sourceMethodNamesArray = sourceMethodNames.encodeAll(new String[sourceMethodNames.getLength()]); } - if (encodeDebugNames) { - namesArray = names.encodeAll(new String[names.getLength()]); - } - install(target, encodedJavaConstants, sourceClassesArray, sourceMethodNamesArray, namesArray, adjuster); + install(target, encodedJavaConstants, sourceClassesArray, sourceMethodNamesArray, adjuster); } @Uninterruptible(reason = "Nonmovable object arrays are not visible to GC until installed in target.") private static void install(CodeInfo target, JavaConstant[] objectConstantsArray, Class[] sourceClassesArray, - String[] sourceMethodNamesArray, String[] namesArray, ReferenceAdjuster adjuster) { + String[] sourceMethodNamesArray, ReferenceAdjuster adjuster) { NonmovableObjectArray frameInfoObjectConstants = adjuster.copyOfObjectConstantArray(objectConstantsArray); NonmovableObjectArray> frameInfoSourceClasses = (sourceClassesArray != null) ? adjuster.copyOfObjectArray(sourceClassesArray) : NonmovableArrays.nullArray(); NonmovableObjectArray frameInfoSourceMethodNames = (sourceMethodNamesArray != null) ? adjuster.copyOfObjectArray(sourceMethodNamesArray) : NonmovableArrays.nullArray(); - NonmovableObjectArray frameInfoNames = (namesArray != null) ? adjuster.copyOfObjectArray(namesArray) : NonmovableArrays.nullArray(); - CodeInfoAccess.setEncodings(target, frameInfoObjectConstants, frameInfoSourceClasses, frameInfoSourceMethodNames, frameInfoNames); + CodeInfoAccess.setEncodings(target, frameInfoObjectConstants, frameInfoSourceClasses, frameInfoSourceMethodNames); } } @@ -334,7 +328,7 @@ private void encodeIPData() { writeSizeEncoding(encodingBuffer, data, entryFlags); writeExceptionOffset(encodingBuffer, data, entryFlags); writeReferenceMapIndex(encodingBuffer, data, entryFlags); - writeDeoptFrameInfo(encodingBuffer, data, entryFlags); + writeEncodedFrameInfo(encodingBuffer, data, entryFlags); } codeInfoIndex = NonmovableArrays.createByteArray(TypeConversion.asU4(indexBuffer.getBytesWritten())); @@ -439,7 +433,7 @@ private static void writeReferenceMapIndex(UnsafeArrayTypeWriter writeBuffer, IP private static int flagsForDeoptFrameInfo(IPData data) { if (data.frameData == null) { return CodeInfoDecoder.FI_NO_DEOPT; - } else if (TypeConversion.isS4(data.frameData.indexInEncodings)) { + } else if (TypeConversion.isS4(data.frameData.encodedFrameInfoIndex)) { if (data.frameData.frame.isDeoptEntry) { return CodeInfoDecoder.FI_DEOPT_ENTRY_INDEX_S4; } else { @@ -450,11 +444,11 @@ private static int flagsForDeoptFrameInfo(IPData data) { } } - private static void writeDeoptFrameInfo(UnsafeArrayTypeWriter writeBuffer, IPData data, int entryFlags) { + private static void writeEncodedFrameInfo(UnsafeArrayTypeWriter writeBuffer, IPData data, int entryFlags) { switch (CodeInfoDecoder.extractFI(entryFlags)) { case CodeInfoDecoder.FI_DEOPT_ENTRY_INDEX_S4: case CodeInfoDecoder.FI_INFO_ONLY_INDEX_S4: - writeBuffer.putS4(data.frameData.indexInEncodings); + writeBuffer.putS4(data.frameData.encodedFrameInfoIndex); break; } } @@ -519,7 +513,7 @@ static void verifyMethod(SharedMethod method, CompilationResult compilation, int private static void verifyFrame(CompilationResult compilation, BytecodeFrame expectedFrame, FrameInfoQueryResult actualFrame, BitSet visitedVirtualObjects) { assert (expectedFrame == null) == (actualFrame == null); - if (expectedFrame == null || !actualFrame.needLocalValues) { + if (expectedFrame == null || !actualFrame.hasLocalValueInfo()) { return; } verifyFrame(compilation, expectedFrame.caller(), actualFrame.getCaller(), visitedVirtualObjects); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoImpl.java index a96da5602940..fd3e06607dc3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoImpl.java @@ -194,12 +194,6 @@ interface CodeInfoImpl extends CodeInfo { @RawField void setFrameInfoSourceMethodNames(NonmovableObjectArray frameInfoSourceMethodNames); - @RawField - NonmovableObjectArray getFrameInfoNames(); - - @RawField - void setFrameInfoNames(NonmovableObjectArray frameInfoNames); - @RawField int getState(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoOffsets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoOffsets.java index 2f68861169b7..0e5b2a6e03b4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoOffsets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoOffsets.java @@ -59,10 +59,6 @@ public static long frameInfoSourceMethodNames() { return OffsetOf.get(CodeInfoImpl.class, "FrameInfoSourceMethodNames"); } - public static long frameInfoNames() { - return OffsetOf.get(CodeInfoImpl.class, "FrameInfoNames"); - } - public static long deoptimizationObjectConstants() { return OffsetOf.get(CodeInfoImpl.class, "DeoptimizationObjectConstants"); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index d70629670fc0..ba39aa63e0e5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -292,6 +292,5 @@ public void afterCompilation(AfterCompilationAccess config) { config.registerAsImmutable(imageInfo.frameInfoObjectConstants); config.registerAsImmutable(imageInfo.frameInfoSourceClasses); config.registerAsImmutable(imageInfo.frameInfoSourceMethodNames); - config.registerAsImmutable(imageInfo.frameInfoNames); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java index 5fad47de76dc..ed582a8518f3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoDecoder.java @@ -46,11 +46,32 @@ public class FrameInfoDecoder { + protected static final int BCI_SHIFT = 2; + protected static final int DURING_CALL_MASK = 2; + protected static final int RETHROW_EXCEPTION_MASK = 1; + protected static final int NO_CALLER_BCI = -1; protected static final int NO_LOCAL_INFO_BCI = -2; + /** + * Differentiates between compressed and uncompressed frame slices. See + * {@link #isCompressedFrameSlice(int)} for more information. + */ + protected static final int UNCOMPRESSED_FRAME_SLICE_MARKER = -1; + /** + * Value added to source line to guarantee the value is greater than zero. + */ + protected static final int COMPRESSED_SOURCE_LINE_ADDEND = 2; + protected static boolean isFrameInfoMatch(long frameInfoIndex, NonmovableArray frameInfoEncodings, long searchEncodedBci) { NonmovableByteArrayTypeReader readBuffer = new NonmovableByteArrayTypeReader(frameInfoEncodings, frameInfoIndex); + int firstValue = readBuffer.getSVInt(); + if (isCompressedFrameSlice(firstValue)) { + /* Compressed frame slices have no local bci information. */ + return false; + } + + /* Read encoded bci from uncompressed frame slice. */ long actualEncodedBci = readBuffer.getSV(); assert actualEncodedBci != NO_CALLER_BCI; @@ -138,7 +159,82 @@ public void decodeConstant(ValueInfo valueInfo, NonmovableObjectArray frameIn static final HeapBasedValueInfoAllocator HeapBasedValueInfoAllocator = new HeapBasedValueInfoAllocator(); protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, TypeReader readBuffer, CodeInfo info, - FrameInfoQueryResultAllocator resultAllocator, ValueInfoAllocator valueInfoAllocator, boolean fetchFirstFrame) { + FrameInfoQueryResultAllocator resultAllocator, ValueInfoAllocator valueInfoAllocator) { + return decodeFrameInfo(isDeoptEntry, readBuffer, info, resultAllocator, valueInfoAllocator, new CodeInfoAccess.FrameInfoState()); + } + + protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, TypeReader readBuffer, CodeInfo info, + FrameInfoQueryResultAllocator resultAllocator, ValueInfoAllocator valueInfoAllocator, CodeInfoAccess.FrameInfoState state) { + if (state.isFirstFrame) { + state.firstValue = readBuffer.getSVInt(); + } + + FrameInfoQueryResult result; + if (isCompressedFrameSlice(state.firstValue)) { + result = decodeCompressedFrameInfo(isDeoptEntry, readBuffer, info, resultAllocator, state); + } else { + result = decodeUncompressedFrameInfo(isDeoptEntry, readBuffer, info, resultAllocator, valueInfoAllocator, state); + } + state.isFirstFrame = false; + + return result; + } + + /* + * See (FrameInfoEncoder.CompressedFrameInfoEncodingMedata) for more information about the + * compressed encoding format. + */ + private static FrameInfoQueryResult decodeCompressedFrameInfo(boolean isDeoptEntry, TypeReader readBuffer, CodeInfo info, + FrameInfoQueryResultAllocator resultAllocator, CodeInfoAccess.FrameInfoState state) { + FrameInfoQueryResult result = null; + FrameInfoQueryResult prev = null; + + while (!state.isDone) { + FrameInfoQueryResult cur = resultAllocator.newFrameInfoQueryResult(); + if (cur == null) { + return result; + } + + assert encodeSourceReferences(); + cur.encodedBci = NO_LOCAL_INFO_BCI; + cur.isDeoptEntry = isDeoptEntry; + + final int sourceClassIndex; + if (state.isFirstFrame) { + sourceClassIndex = state.firstValue; + } else { + sourceClassIndex = readBuffer.getSVInt(); + assert !isDeoptEntry : "Deoptimization entry must not have inlined frames"; + } + + final int sourceMethodNameIndex = readBuffer.getSVInt(); + final int encodedSourceLineNumber = readBuffer.getSVInt(); + final int sourceLineNumber = decodeCompressedSourceLineNumber(encodedSourceLineNumber); + + cur.sourceClassIndex = sourceClassIndex; + cur.sourceMethodNameIndex = sourceMethodNameIndex; + + cur.sourceClass = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), sourceClassIndex); + cur.sourceMethodName = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), sourceMethodNameIndex); + cur.sourceLineNumber = sourceLineNumber; + + if (prev == null) { + // first frame read during this invocation + result = cur; + } else { + prev.caller = cur; + } + prev = cur; + + state.isDone = encodedSourceLineNumber < 0; + state.isFirstFrame = false; + } + + return result; + } + + private static FrameInfoQueryResult decodeUncompressedFrameInfo(boolean isDeoptEntry, TypeReader readBuffer, CodeInfo info, + FrameInfoQueryResultAllocator resultAllocator, ValueInfoAllocator valueInfoAllocator, CodeInfoAccess.FrameInfoState state) { FrameInfoQueryResult result = null; FrameInfoQueryResult prev = null; ValueInfo[][] virtualObjects = null; @@ -154,12 +250,12 @@ protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, Type return result; } + assert state.isFirstFrame || !isDeoptEntry : "Deoptimization entry must not have inlined frames"; + cur.encodedBci = encodedBci; cur.isDeoptEntry = isDeoptEntry; final boolean needLocalValues = encodedBci != NO_LOCAL_INFO_BCI; - cur.needLocalValues = needLocalValues; - int curValueInfosLenght = 0; if (needLocalValues) { cur.numLocks = readBuffer.getUVInt(); @@ -181,39 +277,25 @@ protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, Type cur.deoptMethodOffset = deoptMethodIndex; } - curValueInfosLenght = readBuffer.getUVInt(); - cur.valueInfos = decodeValues(valueInfoAllocator, curValueInfosLenght, readBuffer, CodeInfoAccess.getFrameInfoObjectConstants(info)); + int curValueInfosLength = readBuffer.getUVInt(); + cur.valueInfos = decodeValues(valueInfoAllocator, curValueInfosLength, readBuffer, CodeInfoAccess.getFrameInfoObjectConstants(info)); } - if (prev != null) { - prev.caller = cur; - assert !isDeoptEntry : "Deoptimization entry must not have inlined frames"; - } else { - if (!fetchFirstFrame) { - /* CodeInfoDecoder.nextFrameInfo usecase. First frame was fetched previously. */ - result = cur; - } else { - /* This is the first frame, i.e., the top frame that will be returned. */ - result = cur; - - if (needLocalValues) { - int numVirtualObjects = readBuffer.getUVInt(); - virtualObjects = valueInfoAllocator.newValueInfoArrayArray(numVirtualObjects); - for (int i = 0; i < numVirtualObjects; i++) { - int numValues = readBuffer.getUVInt(); - ValueInfo[] decodedValues = decodeValues(valueInfoAllocator, numValues, readBuffer, CodeInfoAccess.getFrameInfoObjectConstants(info)); - if (virtualObjects != null) { - virtualObjects[i] = decodedValues; - } - } + if (state.isFirstFrame && needLocalValues) { + /* This is the first frame, i.e., the top frame that will be returned. */ + int numVirtualObjects = readBuffer.getUVInt(); + virtualObjects = valueInfoAllocator.newValueInfoArrayArray(numVirtualObjects); + for (int i = 0; i < numVirtualObjects; i++) { + int numValues = readBuffer.getUVInt(); + ValueInfo[] decodedValues = decodeValues(valueInfoAllocator, numValues, readBuffer, CodeInfoAccess.getFrameInfoObjectConstants(info)); + if (virtualObjects != null) { + virtualObjects[i] = decodedValues; } } } - prev = cur; cur.virtualObjects = virtualObjects; - final boolean debugNames = needLocalValues && encodeDebugNames(); - if (debugNames || encodeSourceReferences()) { + if (encodeSourceReferences()) { final int sourceClassIndex = readBuffer.getSVInt(); final int sourceMethodNameIndex = readBuffer.getSVInt(); final int sourceLineNumber = readBuffer.getSVInt(); @@ -226,15 +308,15 @@ protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, Type cur.sourceLineNumber = sourceLineNumber; } - if (debugNames) { - for (int i = 0; i < curValueInfosLenght; ++i) { - int nameIndex = readBuffer.getUVInt(); - if (cur.valueInfos != null) { - cur.valueInfos[i].nameIndex = nameIndex; - cur.valueInfos[i].name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoNames(info), nameIndex); - } - } + if (prev == null) { + // first frame read during this invocation + result = cur; + } else { + prev.caller = cur; } + prev = cur; + + state.isFirstFrame = false; } } @@ -267,18 +349,10 @@ private static ValueInfo[] decodeValues(ValueInfoAllocator valueInfoAllocator, i return valueInfos; } - protected static boolean encodeDebugNames() { - return false; - } - protected static boolean encodeSourceReferences() { return SubstrateOptions.StackTrace.getValue(); } - protected static final int BCI_SHIFT = 2; - protected static final int DURING_CALL_MASK = 2; - protected static final int RETHROW_EXCEPTION_MASK = 1; - protected static int decodeBci(long encodedBci) { return TypeConversion.asS4(encodedBci >> BCI_SHIFT); } @@ -291,6 +365,21 @@ protected static boolean decodeRethrowException(long encodedBci) { return (encodedBci & RETHROW_EXCEPTION_MASK) != 0; } + /** + * Complement of (FrameInfoEncode.encodeCompressedSourceLineNumber). + */ + private static int decodeCompressedSourceLineNumber(int sourceLineNumber) { + return Math.abs(sourceLineNumber) - COMPRESSED_SOURCE_LINE_ADDEND; + } + + /** + * Differentiates between compressed and uncompressed frame slice. Uncompressed frame slices are + * start with an {@link #UNCOMPRESSED_FRAME_SLICE_MARKER}. + */ + private static boolean isCompressedFrameSlice(int firstValue) { + return firstValue != UNCOMPRESSED_FRAME_SLICE_MARKER; + } + public static String readableBci(long encodedBci) { return decodeBci(encodedBci) + ((encodedBci & DURING_CALL_MASK) != 0 ? " duringCall" : "") + diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java index cc5eb40a8a18..b52495a6e016 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoEncoder.java @@ -29,13 +29,17 @@ import java.util.List; import java.util.Objects; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.util.FrequencyEncoder; import org.graalvm.compiler.core.common.util.TypeConversion; import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.CalleeSavedRegisters; import com.oracle.svm.core.ReservedRegisters; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.NonmovableArray; import com.oracle.svm.core.c.NonmovableArrays; @@ -66,26 +70,23 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaValue; -import jdk.vm.ci.meta.Local; -import jdk.vm.ci.meta.LocalVariableTable; import jdk.vm.ci.meta.ResolvedJavaMethod; public class FrameInfoEncoder { public abstract static class Customization { - protected boolean shouldStoreMethod() { - return true; - } + /** + * Returns true if the method's deoptimization target should be saved within the debugInfo. + */ + protected abstract boolean storeDeoptTargetMethod(); /** - * Returns true if the given debugInfo should be encoded. + * Returns true if the given local values should be encoded within the debugInfo. * * @param method The method that contains the debugInfo. * @param infopoint The infopoint whose debugInfo that is considered for inclusion. */ - protected boolean shouldInclude(ResolvedJavaMethod method, Infopoint infopoint) { - return true; - } + protected abstract boolean includeLocalValues(ResolvedJavaMethod method, Infopoint infopoint); /** * Returns true if the given debugInfo is a valid entry point for deoptimization (and not @@ -94,21 +95,19 @@ protected boolean shouldInclude(ResolvedJavaMethod method, Infopoint infopoint) * @param method The method that contains the debugInfo. * @param infopoint The infopoint whose debugInfo that is considered for inclusion. */ - protected boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint) { - return false; - } + protected abstract boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint); /** - * Fills the FrameInfoQueryResult.source* and {@link ValueInfo#name} fields. + * Fills the FrameInfoQueryResult.source* fields. */ - protected abstract void fillDebugNames(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo, boolean fillValueNames); + protected abstract void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo); } - public abstract static class NamesFromMethod extends Customization { + public abstract static class SourceFieldsFromMethod extends Customization { private final HostedStringDeduplication stringTable = HostedStringDeduplication.singleton(); @Override - protected void fillDebugNames(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo, boolean fillValueNames) { + protected void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo) { final ResolvedJavaMethod method = bytecodeFrame.getMethod(); final StackTraceElement source = method.asStackTraceElement(bytecodeFrame.getBCI()); @@ -120,30 +119,14 @@ protected void fillDebugNames(BytecodeFrame bytecodeFrame, FrameInfoQueryResult */ resultFrameInfo.sourceMethodName = stringTable.deduplicate(source.getMethodName(), true); resultFrameInfo.sourceLineNumber = source.getLineNumber(); - - if (fillValueNames) { - final LocalVariableTable localVariableTable = bytecodeFrame.getMethod().getLocalVariableTable(); - if (localVariableTable != null) { - Local[] locals = localVariableTable.getLocalsAt(bytecodeFrame.getBCI()); - if (locals != null) { - for (Local local : locals) { - if (local.getSlot() < resultFrameInfo.valueInfos.length) { - resultFrameInfo.valueInfos[local.getSlot()].name = local.getName(); - } else { - assert ValueUtil.isIllegalJavaValue(bytecodeFrame.values[local.getSlot()]); - } - } - } - } - } } protected abstract Class getDeclaringJavaClass(ResolvedJavaMethod method); } - public static class NamesFromImage extends Customization { + public abstract static class SourceFieldsFromImage extends Customization { @Override - protected void fillDebugNames(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo, boolean fillValueNames) { + protected void fillSourceFields(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo) { final int deoptOffsetInImage = ((SharedMethod) bytecodeFrame.getMethod()).getDeoptOffsetInImage(); if (deoptOffsetInImage != 0) { CodeInfoQueryResult targetCodeInfo = CodeInfoTable.lookupDeoptimizationEntrypoint(deoptOffsetInImage, resultFrameInfo.encodedBci); @@ -154,41 +137,180 @@ protected void fillDebugNames(BytecodeFrame bytecodeFrame, FrameInfoQueryResult resultFrameInfo.sourceClass = targetFrameInfo.sourceClass; resultFrameInfo.sourceMethodName = targetFrameInfo.sourceMethodName; resultFrameInfo.sourceLineNumber = targetFrameInfo.sourceLineNumber; - - if (fillValueNames) { - final int minLength = Math.min(resultFrameInfo.valueInfos.length, targetFrameInfo.valueInfos.length); - for (int i = 0; i < minLength; i++) { - resultFrameInfo.valueInfos[i].name = targetFrameInfo.valueInfos[i].name; - } - } } } } } + private static final int UNCOMPRESSED_FRAME_SLICE_INDEX = -1; + static class FrameData { protected DebugInfo debugInfo; protected int totalFrameSize; protected ValueInfo[][] virtualObjects; protected FrameInfoQueryResult frame; - protected long indexInEncodings; + protected long encodedFrameInfoIndex; + protected int frameSliceIndex = UNCOMPRESSED_FRAME_SLICE_INDEX; + } + + private static class SourceFieldData { + final Class sourceClass; + final String sourceMethodName; + final int sourceLineNumber; + final boolean isSliceEnd; + + SourceFieldData(Class sourceClass, String sourceMethodName, int sourceLineNumber, boolean isSliceEnd) { + this.sourceClass = sourceClass; + this.sourceMethodName = sourceMethodName; + this.sourceLineNumber = sourceLineNumber; + this.isSliceEnd = isSliceEnd; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SourceFieldData that = (SourceFieldData) o; + return sourceLineNumber == that.sourceLineNumber && isSliceEnd == that.isSliceEnd && sourceClass.equals(that.sourceClass) && sourceMethodName.equals(that.sourceMethodName); + } + + @Override + public int hashCode() { + return Objects.hash(sourceClass, sourceMethodName, sourceLineNumber, isSliceEnd); + } + } + + /** + * When the local values are not needed to be saved within the frame encoding, there can be + * significant space savings via using an alternative encoding. + * + * Within the "compressed" encoding, only the following three values are saved for each frame: + *
    + *
  1. index to source class
  2. + *
  3. index to method name
  4. + *
  5. source line number
  6. + *
+ * + * Due to inlining, multiple frames may represent a given {@link Infopoint}. Hence, for each + * Infopoint, we call the frame(s) representing it a *frame slice*. During decoding, within the + * compressed encoding the last frame of a given frame slice is indicated by reading a negative + * source line number. + * + * Additional space is saved when multiple Infopoints' frame slices are identical. All + * Infopoints with identical frame slice information will point to the same compressed frame + * encoding. + * + * Within the encoded frame metadata, frame slices stored in both the compressed and the + * original (i.e., *uncompressed*) frame encoding can coexist. To differentiate between + * compressed and uncompressed frame slices, uncompressed frame slices start with the + * {@link FrameInfoDecoder#UNCOMPRESSED_FRAME_SLICE_MARKER}. + */ + private static class CompressedFrameInfoEncodingMetadata { + final List framesToEncode = new ArrayList<>(); + final EconomicMap framesToEncodeIndexMap = EconomicMap.create(Equivalence.DEFAULT); + final List> frameSlices = new ArrayList<>(); + final EconomicMap, Integer> frameSliceIndexMap = EconomicMap.create(Equivalence.DEFAULT); + final FrequencyEncoder sliceFrequency = FrequencyEncoder.createEqualityEncoder(); + + boolean sealed = false; + EconomicMap encodedSliceIndexMap = EconomicMap.create(Equivalence.DEFAULT); + + void addFrameSlice(FrameData data, List slice) { + assert !sealed; + List encodedFrameSlice = new ArrayList<>(); + for (SourceFieldData fieldData : slice) { + Integer fieldDataIndex = framesToEncodeIndexMap.get(fieldData); + if (fieldDataIndex == null) { + fieldDataIndex = framesToEncode.size(); + framesToEncode.add(fieldData); + framesToEncodeIndexMap.put(fieldData, fieldDataIndex); + } + encodedFrameSlice.add(framesToEncode.get(fieldDataIndex)); + } + Integer frameSliceIndex = frameSliceIndexMap.get(encodedFrameSlice); + if (frameSliceIndex == null) { + frameSliceIndex = frameSlices.size(); + frameSlices.add(encodedFrameSlice); + frameSliceIndexMap.put(encodedFrameSlice, frameSliceIndex); + } + data.frameSliceIndex = frameSliceIndex; + sliceFrequency.addObject(frameSliceIndex); + } + + void encodeCompressedData(UnsafeArrayTypeWriter encodingBuffer, Encoders encoders) { + assert !sealed; + sealed = true; + Integer[] sliceOrder = sliceFrequency.encodeAll(new Integer[sliceFrequency.getLength()]); + for (Integer sliceIdx : sliceOrder) { + assert !encodedSliceIndexMap.containsKey(sliceIdx); + encodedSliceIndexMap.put(sliceIdx, encodingBuffer.getBytesWritten()); + + List slice = frameSlices.get(sliceIdx); + for (SourceFieldData fieldData : slice) { + int classIndex = encoders.sourceClasses.getIndex(fieldData.sourceClass); + int methodIndex = encoders.sourceMethodNames.getIndex(fieldData.sourceMethodName); + + VMError.guarantee(classIndex != FrameInfoDecoder.UNCOMPRESSED_FRAME_SLICE_MARKER); + + encodingBuffer.putSV(classIndex); + encodingBuffer.putSV(methodIndex); + encodingBuffer.putSV(encodeCompressedSourceLineNumber(fieldData.sourceLineNumber, fieldData.isSliceEnd)); + } + } + } + + long getEncodingOffset(int sliceIndex) { + assert sealed; + Long encodedSliceIndex = encodedSliceIndexMap.get(sliceIndex); + assert encodedSliceIndex != null; + return encodedSliceIndex; + } + + /** + * When verifying the frame encoding, sourceClassIndex and sourceMethodNameIndex must be + * filled in correctly. + */ + boolean writeFrameVerificationInfo(FrameData data, Encoders encoders) { + int curIdx = 0; + List slice = frameSlices.get(data.frameSliceIndex); + for (FrameInfoQueryResult cur = data.frame; cur != null; cur = cur.caller) { + cur.encodedBci = FrameInfoDecoder.NO_LOCAL_INFO_BCI; + assert cur == data.frame || !cur.isDeoptEntry : "Deoptimization entry information for caller frames is not persisted"; + + cur.sourceClassIndex = encoders.sourceClasses.getIndex(cur.sourceClass); + cur.sourceMethodNameIndex = encoders.sourceMethodNames.getIndex(cur.sourceMethodName); + boolean isSliceEnd = cur.caller == null; + SourceFieldData fieldData = new SourceFieldData(cur.sourceClass, cur.sourceMethodName, cur.sourceLineNumber, isSliceEnd); + assert fieldData.equals(slice.get(curIdx)); + curIdx++; + } + assert frameSlices.get(data.frameSliceIndex).size() == curIdx; + return true; + } } private final Customization customization; private final List allDebugInfos; private final Encoders encoders; + private final CompressedFrameInfoEncodingMetadata frameMetadata; protected FrameInfoEncoder(Customization customization, Encoders encoders) { this.customization = customization; this.encoders = encoders; this.allDebugInfos = new ArrayList<>(); + this.frameMetadata = new CompressedFrameInfoEncodingMetadata(); } protected FrameData addDebugInfo(ResolvedJavaMethod method, Infopoint infopoint, int totalFrameSize) { - final boolean shouldIncludeMethod = customization.shouldInclude(method, infopoint); + final boolean includeLocalValues = customization.includeLocalValues(method, infopoint); final boolean encodeSourceReferences = FrameInfoDecoder.encodeSourceReferences(); - if (!shouldIncludeMethod && !encodeSourceReferences) { + final boolean useCompressedEncoding = SubstrateOptions.UseCompressedFrameEncodings.getValue() && !includeLocalValues; + if (!includeLocalValues && !encodeSourceReferences) { return null; } @@ -197,28 +319,34 @@ protected FrameData addDebugInfo(ResolvedJavaMethod method, Infopoint infopoint, data.debugInfo = debugInfo; data.totalFrameSize = totalFrameSize; data.virtualObjects = new ValueInfo[countVirtualObjects(debugInfo)][]; - data.frame = addFrame(data, debugInfo.frame(), customization.isDeoptEntry(method, infopoint), shouldIncludeMethod); + data.frame = addFrame(data, debugInfo.frame(), customization.isDeoptEntry(method, infopoint), includeLocalValues); - final boolean encodeDebugNames = shouldIncludeMethod && FrameInfoDecoder.encodeDebugNames(); - if (encodeDebugNames || FrameInfoDecoder.encodeSourceReferences()) { + if (encodeSourceReferences) { + List frameSlice = useCompressedEncoding ? new ArrayList<>() : null; BytecodeFrame bytecodeFrame = data.debugInfo.frame(); - for (FrameInfoQueryResult resultFrame = data.frame; bytecodeFrame != null; resultFrame = resultFrame.caller) { - customization.fillDebugNames(bytecodeFrame, resultFrame, encodeDebugNames && shouldIncludeMethod); + for (FrameInfoQueryResult resultFrame = data.frame; resultFrame != null; resultFrame = resultFrame.caller) { + assert bytecodeFrame != null; + customization.fillSourceFields(bytecodeFrame, resultFrame); + + // save source class and method name + final Class sourceClass = resultFrame.sourceClass; + final String sourceMethodName = resultFrame.sourceMethodName; + encoders.sourceClasses.addObject(sourceClass); + encoders.sourceMethodNames.addObject(sourceMethodName); + + // save encoding metadata + if (useCompressedEncoding) { + assert !resultFrame.hasLocalValueInfo(); + final boolean isSliceEnd = resultFrame.caller == null; + final int sourceLineNumber = resultFrame.sourceLineNumber; + SourceFieldData fieldData = new SourceFieldData(sourceClass, sourceMethodName, sourceLineNumber, isSliceEnd); + frameSlice.add(fieldData); + } + bytecodeFrame = bytecodeFrame.caller(); } - - for (FrameInfoQueryResult cur = data.frame; cur != null; cur = cur.caller) { - encoders.sourceClasses.addObject(cur.sourceClass); - encoders.sourceMethodNames.addObject(cur.sourceMethodName); - - if (encodeDebugNames) { - for (ValueInfo valueInfo : cur.valueInfos) { - if (valueInfo.name == null) { - valueInfo.name = ""; - } - encoders.names.addObject(valueInfo.name); - } - } + if (useCompressedEncoding) { + frameMetadata.addFrameSlice(data, frameSlice); } } @@ -260,21 +388,20 @@ private FrameInfoQueryResult addFrame(FrameData data, BytecodeFrame frame, boole result.virtualObjects = data.virtualObjects; result.encodedBci = encodeBci(frame.getBCI(), frame.duringCall, frame.rethrowException); result.isDeoptEntry = isDeoptEntry; - result.needLocalValues = needLocalValues; - - SharedMethod method = (SharedMethod) frame.getMethod(); - if (customization.shouldStoreMethod()) { - result.deoptMethod = method; - encoders.objectConstants.addObject(SubstrateObjectConstant.forObject(method)); - } - result.deoptMethodOffset = method.getDeoptOffsetInImage(); - - result.numLocals = frame.numLocals; - result.numStack = frame.numStack; - result.numLocks = frame.numLocks; ValueInfo[] valueInfos = null; if (needLocalValues) { + SharedMethod method = (SharedMethod) frame.getMethod(); + if (customization.storeDeoptTargetMethod()) { + result.deoptMethod = method; + encoders.objectConstants.addObject(SubstrateObjectConstant.forObject(method)); + } + result.deoptMethodOffset = method.getDeoptOffsetInImage(); + + result.numLocals = frame.numLocals; + result.numStack = frame.numStack; + result.numLocks = frame.numLocks; + JavaValue[] values = frame.values; int numValues = 0; for (int i = values.length; --i >= 0;) { @@ -581,19 +708,27 @@ private static void afterInstallation(CodeInfo info) { private NonmovableArray encodeFrameDatas() { UnsafeArrayTypeWriter encodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + frameMetadata.encodeCompressedData(encodingBuffer, encoders); for (FrameData data : allDebugInfos) { - data.indexInEncodings = encodingBuffer.getBytesWritten(); - encodeFrameData(data, encodingBuffer); + if (data.frameSliceIndex == UNCOMPRESSED_FRAME_SLICE_INDEX) { + data.encodedFrameInfoIndex = encodingBuffer.getBytesWritten(); + encodeUncompressedFrameData(data, encodingBuffer); + } else { + data.encodedFrameInfoIndex = frameMetadata.getEncodingOffset(data.frameSliceIndex); + assert frameMetadata.writeFrameVerificationInfo(data, encoders); + } } NonmovableArray frameInfoEncodings = NonmovableArrays.createByteArray(TypeConversion.asS4(encodingBuffer.getBytesWritten())); encodingBuffer.toByteBuffer(NonmovableArrays.asByteBuffer(frameInfoEncodings)); return frameInfoEncodings; } - private void encodeFrameData(FrameData data, UnsafeArrayTypeWriter encodingBuffer) { + private void encodeUncompressedFrameData(FrameData data, UnsafeArrayTypeWriter encodingBuffer) { + encodingBuffer.putSV(FrameInfoDecoder.UNCOMPRESSED_FRAME_SLICE_MARKER); + for (FrameInfoQueryResult cur = data.frame; cur != null; cur = cur.caller) { assert cur.encodedBci != FrameInfoDecoder.NO_CALLER_BCI : "used as the end marker during decoding"; - final boolean needLocalValues = cur.needLocalValues; + final boolean needLocalValues = cur.hasLocalValueInfo(); if (!needLocalValues) { cur.encodedBci = FrameInfoDecoder.NO_LOCAL_INFO_BCI; } @@ -627,8 +762,7 @@ private void encodeFrameData(FrameData data, UnsafeArrayTypeWriter encodingBuffe } } - final boolean encodeDebugNames = needLocalValues && FrameInfoDecoder.encodeDebugNames(); - if (encodeDebugNames || FrameInfoDecoder.encodeSourceReferences()) { + if (FrameInfoDecoder.encodeSourceReferences()) { final int classIndex = encoders.sourceClasses.getIndex(cur.sourceClass); final int methodIndex = encoders.sourceMethodNames.getIndex(cur.sourceMethodName); @@ -639,13 +773,6 @@ private void encodeFrameData(FrameData data, UnsafeArrayTypeWriter encodingBuffe encodingBuffer.putSV(methodIndex); encodingBuffer.putSV(cur.sourceLineNumber); } - - if (encodeDebugNames) { - for (ValueInfo valueInfo : cur.valueInfos) { - valueInfo.nameIndex = encoders.names.getIndex(valueInfo.name); - encodingBuffer.putUV(valueInfo.nameIndex); - } - } } encodingBuffer.putSV(FrameInfoDecoder.NO_CALLER_BCI); } @@ -695,11 +822,21 @@ public static long encodeBci(int bci, boolean duringCall, boolean rethrowExcepti return (((long) bci) << FrameInfoDecoder.BCI_SHIFT) | (duringCall ? FrameInfoDecoder.DURING_CALL_MASK : 0) | (rethrowException ? FrameInfoDecoder.RETHROW_EXCEPTION_MASK : 0); } + /** + * The source line number also encodes whether the is it the end of or a frame slice or not. The + * original value is incremented to guarantee all source line numbers are greater than 0. + */ + private static int encodeCompressedSourceLineNumber(int sourceLineNumber, boolean isSliceEnd) { + int lineNumberWithAddend = sourceLineNumber + FrameInfoDecoder.COMPRESSED_SOURCE_LINE_ADDEND; + VMError.guarantee(lineNumberWithAddend > 0); + return isSliceEnd ? -(lineNumberWithAddend) : (lineNumberWithAddend); + } + void verifyEncoding(CodeInfo info) { for (FrameData expectedData : allDebugInfos) { FrameInfoQueryResult actualFrame = FrameInfoDecoder.decodeFrameInfo(expectedData.frame.isDeoptEntry, - new ReusableTypeReader(CodeInfoAccess.getFrameInfoEncodings(info), expectedData.indexInEncodings), - info, FrameInfoDecoder.HeapBasedFrameInfoQueryResultAllocator, FrameInfoDecoder.HeapBasedValueInfoAllocator, true); + new ReusableTypeReader(CodeInfoAccess.getFrameInfoEncodings(info), expectedData.encodedFrameInfoIndex), + info, FrameInfoDecoder.HeapBasedFrameInfoQueryResultAllocator, FrameInfoDecoder.HeapBasedValueInfoAllocator); FrameInfoVerifier.verifyFrames(expectedData, expectedData.frame, actualFrame); } } @@ -712,8 +849,8 @@ protected static void verifyFrames(FrameInfoEncoder.FrameData expectedData, Fram while (expectedFrame != null) { assert actualFrame != null; assert expectedFrame.isDeoptEntry == actualFrame.isDeoptEntry; - assert expectedFrame.needLocalValues == actualFrame.needLocalValues; - if (expectedFrame.needLocalValues) { + assert expectedFrame.hasLocalValueInfo() == actualFrame.hasLocalValueInfo(); + if (expectedFrame.hasLocalValueInfo()) { assert expectedFrame.encodedBci == actualFrame.encodedBci; assert expectedFrame.deoptMethod == null && actualFrame.deoptMethod == null || ((expectedFrame.deoptMethod != null) && expectedFrame.deoptMethod.equals(actualFrame.deoptMethod)); @@ -739,7 +876,7 @@ protected static void verifyFrames(FrameInfoEncoder.FrameData expectedData, Fram } assert actualFrame == null; - if (actualTopFrame.needLocalValues) { + if (actualTopFrame.hasLocalValueInfo()) { assert expectedData.virtualObjects.length == actualTopFrame.virtualObjects.length; for (int i = 0; i < expectedData.virtualObjects.length; i++) { verifyValues(expectedData.virtualObjects[i], actualTopFrame.virtualObjects[i]); @@ -759,8 +896,6 @@ private static void verifyValues(ValueInfo[] expectedValues, ValueInfo[] actualV assert expectedValue.isEliminatedMonitor == actualValue.isEliminatedMonitor; assert expectedValue.data == actualValue.data; verifyConstant(expectedValue.value, actualValue.value); - assert Objects.equals(expectedValue.name, actualValue.name); - assert expectedValue.nameIndex == actualValue.nameIndex; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java index 0c71ad083135..9d9a0a1fd920 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/FrameInfoQueryResult.java @@ -99,9 +99,6 @@ public static class ValueInfo { protected boolean isEliminatedMonitor; protected long data; protected JavaConstant value; - protected String name; - /** Index of {@link #name} in {@link FrameInfoDecoder#decodeFrameInfo frameInfoNames}. */ - protected int nameIndex = -1; /** * Returns the type of the value, describing how to access the value. @@ -156,7 +153,6 @@ public JavaConstant getValue() { protected int deoptMethodOffset; protected long encodedBci; protected boolean isDeoptEntry; - protected boolean needLocalValues; protected int numLocals; protected int numStack; protected int numLocks; @@ -182,7 +178,6 @@ public void init() { deoptMethodOffset = 0; encodedBci = 0; isDeoptEntry = false; - needLocalValues = false; numLocals = 0; numStack = 0; numLocks = 0; @@ -287,6 +282,13 @@ public int getNumStack() { return numStack; } + /** + * Returns whether any local value info is present. + */ + public boolean hasLocalValueInfo() { + return valueInfos != null; + } + /** * Returns the local variables and expression stack values. */ @@ -370,11 +372,4 @@ public Log log(Log log) { return log; } - - /** - * Returns the name of the local variable with the given index, for debugging purposes only. - */ - public String getLocalVariableName(int idx) { - return idx < valueInfos.length ? valueInfos[idx].name : null; - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java index 2d1f91c9bb65..cd7c4bfda8f6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfo.java @@ -65,7 +65,6 @@ public class ImageCodeInfo { @UnknownObjectField(types = {Object[].class}) Object[] frameInfoObjectConstants; @UnknownObjectField(types = {Class[].class}) Class[] frameInfoSourceClasses; @UnknownObjectField(types = {String[].class}) String[] frameInfoSourceMethodNames; - @UnknownObjectField(types = {String[].class}) String[] frameInfoNames; @Platforms(Platform.HOSTED_ONLY.class) ImageCodeInfo() { @@ -98,7 +97,6 @@ CodeInfo prepareCodeInfo() { info.setFrameInfoObjectConstants(NonmovableArrays.fromImageHeap(frameInfoObjectConstants)); info.setFrameInfoSourceClasses(NonmovableArrays.fromImageHeap(frameInfoSourceClasses)); info.setFrameInfoSourceMethodNames(NonmovableArrays.fromImageHeap(frameInfoSourceMethodNames)); - info.setFrameInfoNames(NonmovableArrays.fromImageHeap(frameInfoNames)); return info; } @@ -237,16 +235,6 @@ public void setFrameInfoSourceMethodNames(NonmovableObjectArray array) { frameInfoSourceMethodNames = NonmovableArrays.getHostedArray(array); } - @Override - public NonmovableObjectArray getFrameInfoNames() { - return NonmovableArrays.fromImageHeap(frameInfoNames); - } - - @Override - public void setFrameInfoNames(NonmovableObjectArray array) { - frameInfoNames = NonmovableArrays.getHostedArray(array); - } - @Override public void setObjectFields(NonmovableObjectArray fields) { throw VMError.shouldNotReachHere("not supported for image code"); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java index bc6322ac101e..c017746f2c5a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java @@ -173,7 +173,6 @@ public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor v continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoObjectConstants(), visitor); continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoSourceClasses(), visitor); continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoSourceMethodNames(), visitor); - continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoNames(), visitor); continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getDeoptimizationObjectConstants(), visitor); return continueVisiting; } @@ -324,7 +323,6 @@ public static void forEachObjectArray(CodeInfo info, NonmovableArrayAction actio action.apply(impl.getFrameInfoObjectConstants()); action.apply(impl.getFrameInfoSourceClasses()); action.apply(impl.getFrameInfoSourceMethodNames()); - action.apply(impl.getFrameInfoNames()); action.apply(impl.getDeoptimizationObjectConstants()); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index 54787ca1a7a6..cf65e7102af0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -1124,10 +1124,6 @@ private static void printVirtualFrame(Log log, VirtualFrame virtualFrame) { JavaConstant con = virtualFrame.getConstant(i); if (con.getJavaKind() != JavaKind.Illegal) { log.newline().string(" slot ").signed(i); - String name = frameInfo.getLocalVariableName(i); - if (name != null) { - log.string(" ").string(name); - } log.string(" kind: ").string(con.getJavaKind().toString()); if (con.getJavaKind() == JavaKind.Object) { Object val = SubstrateObjectConstant.asObject(con); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/SubstrateStackIntrospection.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/SubstrateStackIntrospection.java index 6819ad7e7bfb..d015dffd0b99 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/SubstrateStackIntrospection.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/SubstrateStackIntrospection.java @@ -362,10 +362,6 @@ public String toString() { JavaConstant con = getLocalConstant(i); if (con.getJavaKind() != JavaKind.Illegal) { result.append("\n local ").append(i); - String name = frameInfo.getLocalVariableName(i); - if (name != null) { - result.append(" ").append(name); - } if (con.getJavaKind() == JavaKind.Object) { if (isVirtual(i)) { result.append(" [virtual object]"); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java index 338e13d45439..339c434efe67 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/ThreadStackPrinter.java @@ -104,6 +104,7 @@ public void decodeConstant(ValueInfo valueInfo, NonmovableObjectArray frameIn private static SingleShotFrameInfoQueryResultAllocator SingleShotFrameInfoQueryResultAllocator = new SingleShotFrameInfoQueryResultAllocator(); private static DummyValueInfoAllocator DummyValueInfoAllocator = new DummyValueInfoAllocator(); + private static CodeInfoAccess.FrameInfoState frameInfoState = new CodeInfoAccess.FrameInfoState(); @Override protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, DeoptimizedFrame deoptFrame) { @@ -111,12 +112,13 @@ protected void logFrame(Log log, Pointer sp, CodePointer ip, CodeInfo codeInfo, logVirtualFrames(log, sp, ip, deoptFrame); } else { frameInfoReader.reset(); - long entryOffset = CodeInfoAccess.initFrameInfoReader(codeInfo, ip, frameInfoReader); - if (entryOffset >= 0) { + frameInfoState.reset(); + CodeInfoAccess.initFrameInfoReader(codeInfo, ip, frameInfoReader, frameInfoState); + if (frameInfoState.entryOffset >= 0) { boolean isFirst = true; FrameInfoQueryResult validResult; SingleShotFrameInfoQueryResultAllocator.reload(); - while ((validResult = CodeInfoAccess.nextFrameInfo(codeInfo, entryOffset, frameInfoReader, SingleShotFrameInfoQueryResultAllocator, DummyValueInfoAllocator, isFirst)) != null) { + while ((validResult = CodeInfoAccess.nextFrameInfo(codeInfo, frameInfoReader, SingleShotFrameInfoQueryResultAllocator, DummyValueInfoAllocator, frameInfoState)) != null) { SingleShotFrameInfoQueryResultAllocator.reload(); if (!isFirst) { log.newline(); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java index d1d104b8e68d..a6580c89399b 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.Map; +import jdk.vm.ci.meta.ResolvedJavaMethod; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.code.CompilationResult.CodeAnnotation; import org.graalvm.compiler.core.common.NumUtil; @@ -241,7 +242,7 @@ private void patchDirectObjectConstants(ObjectConstantsHolder objectConstants, C } private void createCodeChunkInfos(CodeInfo runtimeMethodInfo, ReferenceAdjuster adjuster) { - CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(new FrameInfoEncoder.NamesFromImage()); + CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(new RuntimeFrameInfoCustomization()); codeInfoEncoder.addMethod(method, compilation, 0); codeInfoEncoder.encodeAllAndInstall(runtimeMethodInfo, adjuster); @@ -266,4 +267,21 @@ private void patchData(Map patcher, @SuppressWarnin } } } + + private static class RuntimeFrameInfoCustomization extends FrameInfoEncoder.SourceFieldsFromImage { + @Override + protected boolean storeDeoptTargetMethod() { + return true; + } + + @Override + protected boolean includeLocalValues(ResolvedJavaMethod method, Infopoint infopoint) { + return true; + } + + @Override + protected boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint) { + return false; + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index 8f43ebfeb1d8..9a68d5596432 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -214,7 +214,7 @@ public int getAlignedConstantsSize() { public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord codeSize) { // Build run-time metadata. - FrameInfoCustomization frameInfoCustomization = new FrameInfoCustomization(); + HostedFrameInfoCustomization frameInfoCustomization = new HostedFrameInfoCustomization(); CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(frameInfoCustomization); for (Entry entry : compilations.entrySet()) { final HostedMethod method = entry.getKey(); @@ -435,7 +435,7 @@ public void printCompilationResults() { } } - private static class FrameInfoCustomization extends FrameInfoEncoder.NamesFromMethod { + private static class HostedFrameInfoCustomization extends FrameInfoEncoder.SourceFieldsFromMethod { int numDeoptEntryPoints; int numDuringCallEntryPoints; @@ -447,12 +447,12 @@ protected Class getDeclaringJavaClass(ResolvedJavaMethod method) { } @Override - protected boolean shouldStoreMethod() { + protected boolean storeDeoptTargetMethod() { return false; } @Override - protected boolean shouldInclude(ResolvedJavaMethod method, Infopoint infopoint) { + protected boolean includeLocalValues(ResolvedJavaMethod method, Infopoint infopoint) { CompilationInfo compilationInfo = ((HostedMethod) method).compilationInfo; BytecodeFrame topFrame = infopoint.debugInfo.frame();