diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMNativeImageCodeCache.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMNativeImageCodeCache.java index 226d76d34fac..f0a6e6d6994a 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMNativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMNativeImageCodeCache.java @@ -29,7 +29,6 @@ import static com.oracle.svm.core.graal.llvm.LLVMToolchainUtils.llvmLink; import static com.oracle.svm.core.graal.llvm.LLVMToolchainUtils.llvmOptimize; import static com.oracle.svm.core.graal.llvm.LLVMToolchainUtils.nativeLink; -import static com.oracle.svm.core.util.VMError.intentionallyUnimplemented; import static com.oracle.svm.core.util.VMError.shouldNotReachHereUnexpectedInput; import static com.oracle.svm.hosted.image.NativeImage.RWDATA_CGLOBALS_PARTITION_OFFSET; @@ -318,11 +317,6 @@ public void patchMethods(DebugContext debug, RelocatableBuffer relocs, ObjectFil @Override public NativeTextSectionImpl getTextSectionImpl(RelocatableBuffer buffer, ObjectFile objectFile, NativeImageCodeCache codeCache) { return new NativeTextSectionImpl(buffer, objectFile, codeCache) { - @Override - protected void defineBaseLayerMethodSymbol(String name, Element section, HostedMethod method) { - throw intentionallyUnimplemented(); // ExcludeFromJacocoGeneratedReport - } - @Override protected void defineMethodSymbol(String name, boolean global, Element section, HostedMethod method, CompilationResult result) { ObjectFile.Symbol symbol = objectFile.createUndefinedSymbol(name, 0, true); 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 604fdd7008e0..a471ab0ce67a 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 @@ -27,6 +27,7 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHereUnexpectedInput; import java.util.BitSet; +import java.util.EnumSet; import java.util.Objects; import java.util.TreeMap; import java.util.function.Consumer; @@ -53,6 +54,7 @@ import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.deopt.DeoptEntryInfopoint; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.heap.CodeReferenceMapDecoder; import com.oracle.svm.core.heap.CodeReferenceMapEncoder; @@ -61,7 +63,13 @@ import com.oracle.svm.core.heap.SubstrateReferenceMap; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.imagelayer.BuildingImageLayerPredicate; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.jfr.HasJfrSupport; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SharedMethod; import com.oracle.svm.core.meta.SharedType; @@ -201,12 +209,14 @@ private void encodeAllAndInstall(CodeInfo target, ReferenceAdjuster adjuster) { String[] memberNamesArray = encodeArray(memberNames, String[]::new); String[] otherStringsArray = encodeArray(otherStrings, String[]::new); - /* - * For image code, we currently have a single code info for which method ids start at 0 - * (with 0 meaning invalid). Runtime code info can only reference image methods via - * these same ids and doesn't have its own method table. - */ - int methodTableFirstId = 0; + int methodTableFirstId; + if (ImageLayerBuildingSupport.buildingImageLayer()) { + var idTracker = MethodTableFirstIDTracker.singleton(); + methodTableFirstId = idTracker.startingID; + idTracker.nextStartingId = methodTableFirstId + methods.getLength(); + } else { + methodTableFirstId = 0; + } NonmovableArray methodTable = encodeMethodTable(); install(target, objectConstantsArray, classesArray, memberNamesArray, otherStringsArray, methodTable, methodTableFirstId, adjuster); @@ -863,6 +873,41 @@ private static ValueInfo findActualValue(ValueInfo[] actualObject, UnsignedWord } } +@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingImageLayerPredicate.class) +class MethodTableFirstIDTracker implements LayeredImageSingleton { + public final int startingID; + public int nextStartingId = -1; + + MethodTableFirstIDTracker() { + this(0); + } + + static MethodTableFirstIDTracker singleton() { + return ImageSingletons.lookup(MethodTableFirstIDTracker.class); + } + + private MethodTableFirstIDTracker(int id) { + startingID = id; + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY; + } + + @Override + public PersistFlags preparePersist(ImageSingletonWriter writer) { + assert nextStartingId > 0 : nextStartingId; + writer.writeInt("startingID", nextStartingId); + return PersistFlags.CREATE; + } + + @SuppressWarnings("unused") + public static Object createFromLoader(ImageSingletonLoader loader) { + return new MethodTableFirstIDTracker(loader.readInt("startingID")); + } +} + class CollectingObjectReferenceVisitor implements ObjectReferenceVisitor { protected final SubstrateReferenceMap result = new SubstrateReferenceMap(); 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 78c012ffca04..e424153efc55 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 @@ -288,7 +288,7 @@ protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, Reus FrameInfoQueryResult result; if (CompressedFrameDecoderHelper.isCompressedFrameSlice(state.firstValue)) { - result = decodeCompressedFrameInfo(isDeoptEntry, readBuffer, resultAllocator, state); + result = decodeCompressedFrameInfo(isDeoptEntry, readBuffer, resultAllocator, state, CodeInfoAccess.getMethodTableFirstId(info)); } else { result = decodeUncompressedFrameInfo(isDeoptEntry, readBuffer, info, resultAllocator, valueInfoAllocator, constantAccess, state); } @@ -302,7 +302,8 @@ protected static FrameInfoQueryResult decodeFrameInfo(boolean isDeoptEntry, Reus * compressed encoding format. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static FrameInfoQueryResult decodeCompressedFrameInfo(boolean isDeoptEntry, ReusableTypeReader readBuffer, FrameInfoQueryResultAllocator resultAllocator, FrameInfoState state) { + private static FrameInfoQueryResult decodeCompressedFrameInfo(boolean isDeoptEntry, ReusableTypeReader readBuffer, FrameInfoQueryResultAllocator resultAllocator, FrameInfoState state, + int methodIdAddend) { FrameInfoQueryResult result = null; FrameInfoQueryResult prev = null; @@ -340,13 +341,13 @@ private static FrameInfoQueryResult decodeCompressedFrameInfo(boolean isDeoptEnt int methodId = readBuffer.getSVInt(); VMError.guarantee(!CompressedFrameDecoderHelper.isSharedFramePointer(methodId)); - decodeCompressedFrameData(readBuffer, state, methodId, cur); + decodeCompressedFrameData(readBuffer, state, methodId, cur, methodIdAddend); // jump back to frame slice information readBuffer.setByteIndex(bufferIndexToRestore); bufferIndexToRestore = -1; } else { - decodeCompressedFrameData(readBuffer, state, firstEntry, cur); + decodeCompressedFrameData(readBuffer, state, firstEntry, cur, methodIdAddend); } if (bufferIndexToRestore != -1) { @@ -373,8 +374,8 @@ private static FrameInfoQueryResult newFrameInfoQueryResult(FrameInfoQueryResult } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static void decodeCompressedFrameData(ReusableTypeReader readBuffer, FrameInfoState state, int methodId, FrameInfoQueryResult queryResult) { - queryResult.sourceMethodId = methodId; + private static void decodeCompressedFrameData(ReusableTypeReader readBuffer, FrameInfoState state, int methodId, FrameInfoQueryResult queryResult, int methodIdAddend) { + queryResult.sourceMethodId = methodId + methodIdAddend; int encodedSourceLineNumber = readBuffer.getSVInt(); long compressedBci = readBuffer.getSV(); @@ -400,6 +401,7 @@ private static FrameInfoQueryResult decodeUncompressedFrameInfo(boolean isDeoptE FrameInfoQueryResult prev = null; ValueInfo[][] virtualObjects = null; + int methodIdAddend = CodeInfoAccess.getMethodTableFirstId(info); while (!state.isDone) { long start = readBuffer.getByteIndex(); long encodedBci = readBuffer.getUV(); @@ -460,7 +462,7 @@ private static FrameInfoQueryResult decodeUncompressedFrameInfo(boolean isDeoptE } cur.virtualObjects = virtualObjects; - cur.sourceMethodId = readBuffer.getSVInt(); + cur.sourceMethodId = readBuffer.getSVInt() + methodIdAddend; cur.sourceLineNumber = readBuffer.getSVInt(); if (prev == null) { 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 035b6df760f6..8d891e6b591d 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 @@ -1052,15 +1052,16 @@ void verifyEncoding(CodeInfo info) { for (FrameData expectedData : allDebugInfos) { ReusableTypeReader reader = new ReusableTypeReader(CodeInfoAccess.getFrameInfoEncodings(info), expectedData.encodedFrameInfoIndex); FrameInfoQueryResult actualFrame = FrameInfoDecoder.decodeFrameInfo(expectedData.frame.isDeoptEntry, reader, info, constantAccess); - FrameInfoVerifier.verifyFrames(expectedData, expectedData.frame, actualFrame); + FrameInfoVerifier.verifyFrames(expectedData, expectedData.frame, actualFrame, info); } } } class FrameInfoVerifier { - protected static void verifyFrames(FrameInfoEncoder.FrameData expectedData, FrameInfoQueryResult expectedTopFrame, FrameInfoQueryResult actualTopFrame) { + protected static void verifyFrames(FrameInfoEncoder.FrameData expectedData, FrameInfoQueryResult expectedTopFrame, FrameInfoQueryResult actualTopFrame, CodeInfo info) { FrameInfoQueryResult expectedFrame = expectedTopFrame; FrameInfoQueryResult actualFrame = actualTopFrame; + int methodIdAddend = CodeInfoAccess.getMethodTableFirstId(info); while (expectedFrame != null) { assert actualFrame != null; assert expectedFrame.isDeoptEntry() == actualFrame.isDeoptEntry() : actualFrame; @@ -1079,7 +1080,7 @@ protected static void verifyFrames(FrameInfoEncoder.FrameData expectedData, Fram assert actualFrame.getVirtualObjects() == actualTopFrame.getVirtualObjects() : actualFrame; } - assert expectedFrame.getSourceMethodId() == actualFrame.getSourceMethodId() : actualFrame; + assert expectedFrame.getSourceMethodId() == (actualFrame.getSourceMethodId() - methodIdAddend) : actualFrame; assert expectedFrame.getSourceLineNumber() == actualFrame.getSourceLineNumber() : actualFrame; expectedFrame = expectedFrame.caller; 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 d9860a5fcb92..0525a7377234 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 @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.code; +import java.util.EnumSet; import java.util.List; import org.graalvm.nativeimage.Platform; @@ -31,26 +32,27 @@ import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.word.ComparableWord; import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; import com.oracle.svm.core.BuildPhaseProvider.AfterCompilation; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.c.CIsolateData; -import com.oracle.svm.core.c.CIsolateDataFactory; 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.heap.UnknownObjectField; import com.oracle.svm.core.heap.UnknownPrimitiveField; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; import com.oracle.svm.core.nmt.NmtCategory; import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.word.Word; -public class ImageCodeInfo { +public class ImageCodeInfo implements MultiLayeredImageSingleton, UnsavedSingleton { public static final String CODE_INFO_NAME = "image code"; - private final CIsolateData runtimeCodeInfo = CIsolateDataFactory.createStruct("runtimeCodeInfo", CodeInfoImpl.class); - @Platforms(Platform.HOSTED_ONLY.class) // private final HostedImageCodeInfo hostedImageCodeInfo = new HostedImageCodeInfo(); @@ -85,28 +87,49 @@ public class ImageCodeInfo { @Uninterruptible(reason = "Executes during isolate creation.") CodeInfo prepareCodeInfo() { - CodeInfoImpl info = runtimeCodeInfo.get(); - assert info.getCodeStart().isNull() : "already initialized"; - - info.setObjectFields(NonmovableArrays.fromImageHeap(objectFields)); - info.setCodeStart(codeStart); - info.setCodeSize(codeSize); - info.setDataOffset(dataOffset); - info.setDataSize(dataSize); - info.setCodeAndDataMemorySize(codeAndDataMemorySize); - info.setCodeInfoIndex(NonmovableArrays.fromImageHeap(codeInfoIndex)); - info.setCodeInfoEncodings(NonmovableArrays.fromImageHeap(codeInfoEncodings)); - info.setStackReferenceMapEncoding(NonmovableArrays.fromImageHeap(referenceMapEncoding)); - info.setFrameInfoEncodings(NonmovableArrays.fromImageHeap(frameInfoEncodings)); - info.setObjectConstants(NonmovableArrays.fromImageHeap(objectConstants)); - info.setClasses(NonmovableArrays.fromImageHeap(classes)); - info.setMemberNames(NonmovableArrays.fromImageHeap(memberNames)); - info.setOtherStrings(NonmovableArrays.fromImageHeap(otherStrings)); - info.setMethodTable(NonmovableArrays.fromImageHeap(methodTable)); - info.setMethodTableFirstId(methodTableFirstId); - info.setIsAOTImageCode(true); - - return info; + if (!ImageLayerBuildingSupport.buildingImageLayer()) { + ImageCodeInfo imageCodeInfo = CodeInfoTable.getImageCodeCache(); + CodeInfoImpl codeInfo = ImageCodeInfoStorage.get(); + return ImageCodeInfo.prepareCodeInfo0(imageCodeInfo, codeInfo, WordFactory.nullPointer()); + } else { + ImageCodeInfo[] imageCodeInfos = MultiLayeredImageSingleton.getAllLayers(ImageCodeInfo.class); + ImageCodeInfoStorage[] runtimeCodeInfos = MultiLayeredImageSingleton.getAllLayers(ImageCodeInfoStorage.class); + int size = imageCodeInfos.length; + for (int i = 0; i < size; i++) { + ImageCodeInfo imageCodeInfo = imageCodeInfos[i]; + CodeInfoImpl codeInfoImpl = runtimeCodeInfos[i].getData(); + CodeInfoImpl nextCodeInfoImpl = i + 1 < size ? runtimeCodeInfos[i + 1].getData() : WordFactory.nullPointer(); + + ImageCodeInfo.prepareCodeInfo0(imageCodeInfo, codeInfoImpl, nextCodeInfoImpl); + } + return runtimeCodeInfos[0].getData(); + } + } + + @Uninterruptible(reason = "Executes during isolate creation.") + private static CodeInfo prepareCodeInfo0(ImageCodeInfo imageCodeInfo, CodeInfoImpl infoImpl, CodeInfo next) { + assert infoImpl.getCodeStart().isNull() : "already initialized"; + + infoImpl.setObjectFields(NonmovableArrays.fromImageHeap(imageCodeInfo.objectFields)); + infoImpl.setCodeStart(imageCodeInfo.codeStart); + infoImpl.setCodeSize(imageCodeInfo.codeSize); + infoImpl.setDataOffset(imageCodeInfo.dataOffset); + infoImpl.setDataSize(imageCodeInfo.dataSize); + infoImpl.setCodeAndDataMemorySize(imageCodeInfo.codeAndDataMemorySize); + infoImpl.setCodeInfoIndex(NonmovableArrays.fromImageHeap(imageCodeInfo.codeInfoIndex)); + infoImpl.setCodeInfoEncodings(NonmovableArrays.fromImageHeap(imageCodeInfo.codeInfoEncodings)); + infoImpl.setStackReferenceMapEncoding(NonmovableArrays.fromImageHeap(imageCodeInfo.referenceMapEncoding)); + infoImpl.setFrameInfoEncodings(NonmovableArrays.fromImageHeap(imageCodeInfo.frameInfoEncodings)); + infoImpl.setObjectConstants(NonmovableArrays.fromImageHeap(imageCodeInfo.objectConstants)); + infoImpl.setClasses(NonmovableArrays.fromImageHeap(imageCodeInfo.classes)); + infoImpl.setMemberNames(NonmovableArrays.fromImageHeap(imageCodeInfo.memberNames)); + infoImpl.setOtherStrings(NonmovableArrays.fromImageHeap(imageCodeInfo.otherStrings)); + infoImpl.setMethodTable(NonmovableArrays.fromImageHeap(imageCodeInfo.methodTable)); + infoImpl.setMethodTableFirstId(imageCodeInfo.methodTableFirstId); + infoImpl.setIsAOTImageCode(true); + infoImpl.setNextImageCodeInfo(next); + + return infoImpl; } /** @@ -126,6 +149,11 @@ public List getTotalByteArrayLengths() { return List.of(codeInfoIndex.length, codeInfoEncodings.length, referenceMapEncoding.length, frameInfoEncodings.length, methodTable.length); } + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.ALL_ACCESS; + } + /** * Pure-hosted {@link CodeInfo} to collect and persist image code metadata in * {@link ImageCodeInfo} and provide accesses during image generation. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfoStorage.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfoStorage.java new file mode 100644 index 000000000000..0aa9eba718b4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/ImageCodeInfoStorage.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.code; + +import java.util.EnumSet; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.Uninterruptible; +import com.oracle.svm.core.c.CIsolateData; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; + +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.core.common.NumUtil; +import jdk.graal.compiler.word.Word; +import jdk.vm.ci.meta.JavaKind; + +/** + * Note we now store this separately from {@link CIsolateData} so that it can be stored in a + * multi-layered image singleton. + */ +public class ImageCodeInfoStorage implements MultiLayeredImageSingleton, UnsavedSingleton { + + private final byte[] data; + + /* + * Always align structures to 8 bytes + */ + static final int ALIGNMENT = Long.BYTES; + + ImageCodeInfoStorage(int dataSize) { + data = new byte[dataSize]; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + static CodeInfoImpl get() { + return ImageSingletons.lookup(ImageCodeInfoStorage.class).getData(); + } + + @Fold + protected static UnsignedWord offset() { + return WordFactory.unsigned(calculateOffset()); + } + + static int calculateOffset() { + return NumUtil.roundUp(ConfigurationValues.getObjectLayout().getArrayBaseOffset(JavaKind.Byte), ALIGNMENT); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + CodeInfoImpl getData() { + Pointer base = Word.objectToUntrackedPointer(data).add(offset()); + + return (CodeInfoImpl) base; + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS_ONLY; + } +} + +@AutomaticallyRegisteredFeature +class ImageCodeInfoStorageFeature implements InternalFeature, UnsavedSingleton, FeatureSingleton { + + @Override + public void duringSetup(DuringSetupAccess access) { + long size = SizeOf.unsigned(CodeInfoImpl.class).rawValue(); + int arrayBaseOffset = ConfigurationValues.getObjectLayout().getArrayBaseOffset(JavaKind.Byte); + int actualOffset = ImageCodeInfoStorage.calculateOffset(); + int addend = actualOffset - arrayBaseOffset; + + ImageSingletons.add(ImageCodeInfoStorage.class, new ImageCodeInfoStorage(NumUtil.safeToInt(size + addend))); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/BuildingExtensionLayerPredicate.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/BuildingExtensionLayerPredicate.java new file mode 100644 index 000000000000..74135cef07a0 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/BuildingExtensionLayerPredicate.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.imagelayer; + +import java.util.function.BooleanSupplier; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +@Platforms(Platform.HOSTED_ONLY.class) +public class BuildingExtensionLayerPredicate implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return ImageLayerBuildingSupport.buildingExtensionLayer(); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java index 77541bf16220..cdbd53c1a02a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/LIRNativeImageCodeCache.java @@ -475,12 +475,6 @@ private NativeTextSectionImpl(RelocatableBuffer buffer, ObjectFile objectFile, N super(buffer, objectFile, codeCache); } - @Override - protected void defineBaseLayerMethodSymbol(String name, ObjectFile.Element section, HostedMethod method) { - VMError.guarantee(method.wrapped.isInBaseLayer(), "Expecting a base layer method, found %s", method); - objectFile.createUndefinedSymbol(name, 0, true); - } - @Override protected void defineMethodSymbol(String name, boolean global, ObjectFile.Element section, HostedMethod method, CompilationResult result) { final int size = result == null ? 0 : result.getTargetCodeSize(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index 3cdb572d48e4..a4672cbbacea 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -108,6 +108,7 @@ import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.image.RelocatableBuffer.Info; import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; +import com.oracle.svm.hosted.imagelayer.PriorLayerSymbolTracker; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; @@ -508,6 +509,11 @@ public void build(String imageName, DebugContext debug) { defineDataSymbol(Isolates.IMAGE_HEAP_WRITABLE_BEGIN_SYMBOL_NAME, heapSection, heapLayout.getWritableOffset() - heapLayout.getStartOffset()); defineDataSymbol(Isolates.IMAGE_HEAP_WRITABLE_END_SYMBOL_NAME, heapSection, heapLayout.getWritableOffset() + heapLayout.getWritableSize() - heapLayout.getStartOffset()); + var symbolTracker = PriorLayerSymbolTracker.singletonOrNull(); + if (symbolTracker != null) { + symbolTracker.defineSymbols(objectFile); + } + // Mark the sections with the relocations from the maps. markRelocationSitesFromBuffer(textBuffer, textImpl); markRelocationSitesFromBuffer(roDataBuffer, roDataImpl); @@ -901,8 +907,6 @@ public byte[] getOrDecideContent(Map alreadyDecided, return getContent(); } - protected abstract void defineBaseLayerMethodSymbol(String name, Element section, HostedMethod method); - protected abstract void defineMethodSymbol(String name, boolean global, Element section, HostedMethod method, CompilationResult result); @SuppressWarnings("try") @@ -945,10 +949,10 @@ protected void writeTextSection(DebugContext debug, final Section textSection, f // 1. fq with return type if (codeCache.getBaseLayerMethods() != null) { - // define base layer methods symbols + // register base layer methods symbols + var symbolTracker = PriorLayerSymbolTracker.singletonOrNull(); for (HostedMethod current : codeCache.getBaseLayerMethods()) { - final String symName = localSymbolNameForMethod(current); - defineBaseLayerMethodSymbol(symName, textSection, current); + symbolTracker.registerPriorLayerReference(current); } } 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 2cbcf473853e..25381ec6ddd7 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 @@ -96,6 +96,7 @@ import com.oracle.svm.hosted.code.SubstrateCompilationDirectives; import com.oracle.svm.hosted.code.SubstrateCompilationDirectives.DeoptSourceFrameInfo; import com.oracle.svm.hosted.image.NativeImage.NativeTextSectionImpl; +import com.oracle.svm.hosted.imagelayer.PriorLayerSymbolTracker; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; @@ -398,9 +399,13 @@ protected void buildRuntimeMetadata(DebugContext debug, SnippetReflectionProvide } })); + var symbolTracker = PriorLayerSymbolTracker.singletonOrNull(); configurationExecutables.forEach(((analysisMethod, reflectMethod) -> { if (includedMethods.add(analysisMethod)) { HostedMethod method = hUniverse.lookup(analysisMethod); + if (analysisMethod.isInBaseLayer()) { + symbolTracker.registerPriorLayerReference(method); + } Object accessor = reflectionSupport.getAccessor(analysisMethod); runtimeMetadataEncoder.addReflectionExecutableMetadata(hMetaAccess, method, reflectMethod, accessor); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java index 9f5d1e80cf46..9ba53597e6c6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/RelocatableBuffer.java @@ -57,7 +57,7 @@ public RelocatableBuffer(long size, ByteOrder byteOrder) { } public void addRelocationWithoutAddend(int key, ObjectFile.RelocationKind relocationKind, Object targetObject) { - relocations.put(key, new Info(relocationKind, 0L, targetObject)); + addRelocationWithAddend(key, relocationKind, 0, targetObject); } public void addRelocationWithAddend(int key, ObjectFile.RelocationKind relocationKind, long addend, Object targetObject) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/PriorLayerSymbolTracker.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/PriorLayerSymbolTracker.java new file mode 100644 index 000000000000..0b6a120d911d --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/PriorLayerSymbolTracker.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.imagelayer; + +import static com.oracle.svm.hosted.image.NativeImage.localSymbolNameForMethod; + +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.objectfile.ObjectFile; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; +import com.oracle.svm.core.imagelayer.BuildingExtensionLayerPredicate; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.meta.HostedMethod; + +/** + * Tracks references to symbols defined in prior layers so that we can define them as undefined + * symbols in the layer. + */ +@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingExtensionLayerPredicate.class) +public class PriorLayerSymbolTracker implements UnsavedSingleton { + + private final Set referencedMethods = ConcurrentHashMap.newKeySet(); + boolean sealed = false; + + public static PriorLayerSymbolTracker singletonOrNull() { + if (ImageSingletons.contains(PriorLayerSymbolTracker.class)) { + return ImageSingletons.lookup(PriorLayerSymbolTracker.class); + } + + return null; + } + + public void registerPriorLayerReference(HostedMethod method) { + VMError.guarantee(method.getWrapped().isInBaseLayer(), "Can only register methods in a prior layer"); + VMError.guarantee(!sealed, "Too late to register methods"); + referencedMethods.add(method); + } + + public void defineSymbols(ObjectFile objectFile) { + sealed = true; + referencedMethods.forEach(m -> objectFile.createUndefinedSymbol(localSymbolNameForMethod(m), 0, true)); + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index cb6743c28dc7..e1d4381ffe72 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -419,14 +419,22 @@ private void registerMethod(ConfigurationCondition cnd, boolean queriedOnly, Exe } AnalysisMethod analysisMethod = metaAccess.lookupJavaMethod(reflectExecutable); - var exists = registeredMethods.containsKey(analysisMethod); - var conditionalValue = registeredMethods.computeIfAbsent(analysisMethod, (t) -> new ConditionalRuntimeValue<>(RuntimeConditionSet.emptySet(), reflectExecutable)); + boolean registered = false; + ConditionalRuntimeValue conditionalValue = registeredMethods.get(analysisMethod); + if (conditionalValue == null) { + var newConditionalValue = new ConditionalRuntimeValue<>(RuntimeConditionSet.emptySet(), reflectExecutable); + conditionalValue = registeredMethods.putIfAbsent(analysisMethod, newConditionalValue); + if (conditionalValue == null) { + conditionalValue = newConditionalValue; + registered = true; + } + } if (!queriedOnly) { /* queryOnly methods are conditioned by the type itself */ conditionalValue.getConditions().addCondition(cnd); } - if (!exists) { + if (registered) { registerTypesForMethod(analysisMethod, reflectExecutable); AnalysisType declaringType = analysisMethod.getDeclaringClass(); Class declaringClass = declaringType.getJavaClass();