diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index e5a4331157a3..1e5d040632d5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -30,6 +30,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -1304,7 +1305,7 @@ public AnalysisMethod findConstructor(Signature signature) { } public Set getOpenTypeWorldDispatchTableMethods() { - AnalysisError.guarantee(dispatchTableMethods != null); + Objects.requireNonNull(dispatchTableMethods); return dispatchTableMethods; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java index 654d23130c67..fde6491cd917 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java @@ -450,7 +450,7 @@ public void persistMethodInfo(Collection methods) { } else { if (priorIdx != vTableIndex) { if (generateErrorMessage()) { - String message = String.format("VTable Index Mismatch %s. prior: %s new: %s", method.format("%H.%n"), priorIdx, vTableIndex); + String message = String.format("VTable Index Mismatch %s. prior: %s new: %s", method.format("%H.%n(%p)"), priorIdx, vTableIndex); if (logErrorMessages()) { System.out.println(message); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java index e4c654067d75..1716c5c2cc82 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java @@ -67,6 +67,7 @@ import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.image.NativeImageHeap; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; +import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; import com.oracle.svm.hosted.lambda.LambdaSubstitutionType; import com.oracle.svm.hosted.lambda.StableLambdaProxyNameFeature; import com.oracle.svm.hosted.meta.HostedField; @@ -183,6 +184,14 @@ protected boolean shouldPersistMethod(AnalysisMethod method) { return false; } + @Override + public void persistMethod(AnalysisMethod method) { + super.persistMethod(method); + + // register this method as persisted for name resolution + HostedDynamicLayerInfo.singleton().recordPersistedMethod(hUniverse.lookup(method)); + } + @Override protected void persistField(AnalysisField field, EconomicMap fieldMap) { HostedField hostedField = hUniverse.lookup(field); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java index dc8cfd9d4d8c..2ab347f624e1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java @@ -35,6 +35,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.word.PointerBase; @@ -57,25 +59,29 @@ import com.oracle.svm.hosted.c.CGlobalDataFeature; import com.oracle.svm.hosted.image.NativeImage; import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedMethodNameFactory.MethodNameInfo; import jdk.graal.compiler.debug.Assertions; public class HostedDynamicLayerInfo extends DynamicImageLayerInfo implements LayeredImageSingleton { private final Map methodIdToOffsetMap; + private final ConcurrentHashMap methodIdToNameInfoMap; private final CGlobalData cGlobalData; private final Set priorLayerHostedMethods = new HashSet<>(); + private boolean persisted = false; HostedDynamicLayerInfo() { - this(0, null, new HashMap<>()); + this(0, null, new HashMap<>(), new ConcurrentHashMap<>()); } public static HostedDynamicLayerInfo singleton() { return (HostedDynamicLayerInfo) ImageSingletons.lookup(DynamicImageLayerInfo.class); } - private HostedDynamicLayerInfo(int layerNumber, String codeSectionStartSymbol, Map methodIdToOffsetMap) { + private HostedDynamicLayerInfo(int layerNumber, String codeSectionStartSymbol, Map methodIdToOffsetMap, ConcurrentHashMap methodIdToNameInfoMap) { super(layerNumber); this.methodIdToOffsetMap = methodIdToOffsetMap; + this.methodIdToNameInfoMap = methodIdToNameInfoMap; cGlobalData = codeSectionStartSymbol == null ? null : CGlobalDataFactory.forSymbol(codeSectionStartSymbol); } @@ -95,7 +101,23 @@ public boolean compiledInPriorLayer(AnalysisMethod aMethod) { return methodIdToOffsetMap.containsKey(aMethod.getId()); } - void registerOffset(HostedMethod method) { + public MethodNameInfo loadMethodNameInfo(AnalysisMethod method) { + return methodIdToNameInfoMap.get(method.getId()); + } + + public void recordPersistedMethod(HostedMethod hMethod) { + assert !persisted : "Too late to record this information"; + MethodNameInfo info = new MethodNameInfo(hMethod.getName(), hMethod.getUniqueShortName()); + var prev = methodIdToNameInfoMap.put(hMethod.getWrapped().getId(), info); + // will have to change for multiple layers + assert prev == null : prev; + } + + public Set getReservedNames() { + return methodIdToNameInfoMap.values().stream().map(MethodNameInfo::uniqueShortName).collect(Collectors.toUnmodifiableSet()); + } + + void registerCompilation(HostedMethod method) { assert BuildPhaseProvider.isCompileQueueFinished(); int offset = method.getCodeAddressOffset(); int methodID = method.getWrapped().getId(); @@ -143,6 +165,7 @@ boolean verifyUniqueOffsets(Collection methods) { @Override public PersistFlags preparePersist(ImageSingletonWriter writer) { + persisted = true; /* * When there are multiple shared layers we will need to store the starting code offset of * each layer. @@ -163,20 +186,33 @@ public PersistFlags preparePersist(ImageSingletonWriter writer) { * Write out all method offsets. */ List offsets = new ArrayList<>(methodIdToOffsetMap.size()); - List methodIDs = new ArrayList<>(methodIdToOffsetMap.size()); + List methodOffsetIds = new ArrayList<>(methodIdToOffsetMap.size()); methodIdToOffsetMap.forEach((key, value) -> { - methodIDs.add(key); + methodOffsetIds.add(key); offsets.add(value); }); - writer.writeIntList("methodIDs", methodIDs); + writer.writeIntList("methodOffsetIDs", methodOffsetIds); writer.writeIntList("offsets", offsets); + /* + * Write out all persisted method names + */ + List methodNameIds = new ArrayList<>(methodIdToNameInfoMap.size()); + List names = new ArrayList<>(methodIdToNameInfoMap.size() * 2); + methodIdToNameInfoMap.forEach((key, value) -> { + methodNameIds.add(key); + names.add(value.name()); + names.add(value.uniqueShortName()); + }); + writer.writeIntList("methodNameIDs", methodNameIds); + writer.writeStringList("names", names); + return PersistFlags.CREATE; } @SuppressWarnings("unused") public static Object createFromLoader(ImageSingletonLoader loader) { - assert loader.readIntList("offsets").size() == loader.readIntList("methodIDs").size() : Assertions.errorMessage("Offsets and methodIDs are incompatible", loader.readIntList("offsets"), + assert loader.readIntList("offsets").size() == loader.readIntList("methodOffsetIDs").size() : Assertions.errorMessage("Offsets and methodIDs are incompatible", loader.readIntList("offsets"), loader.readIntList("methodIDs")); int layerNumber = loader.readInt("nextLayerNumber"); @@ -187,16 +223,32 @@ public static Object createFromLoader(ImageSingletonLoader loader) { * Load the offsets of all methods in the prior layers. */ var offsets = loader.readIntList("offsets").iterator(); - var methodIDs = loader.readIntList("methodIDs").iterator(); + var methodOffsetIds = loader.readIntList("methodOffsetIDs").iterator(); Map initialMethodIdToOffsetMap = new HashMap<>(); while (offsets.hasNext()) { - int methodId = methodIDs.next(); + int methodId = methodOffsetIds.next(); int offset = offsets.next(); - initialMethodIdToOffsetMap.put(methodId, offset); + var prev = initialMethodIdToOffsetMap.put(methodId, offset); + assert prev == null; + } + + /* + * Load the names of all methods in the prior layers. + */ + var names = loader.readStringList("names").iterator(); + var methodNameIds = loader.readIntList("methodNameIDs").iterator(); + ConcurrentHashMap initialMethodIdToMethodNameMap = new ConcurrentHashMap<>(); + + while (methodNameIds.hasNext()) { + int methodId = methodNameIds.next(); + String name = names.next(); + String uniqueShortName = names.next(); + var prev = initialMethodIdToMethodNameMap.put(methodId, new MethodNameInfo(name, uniqueShortName)); + assert prev == null; } - return new HostedDynamicLayerInfo(layerNumber, codeSectionStartSymbol, initialMethodIdToOffsetMap); + return new HostedDynamicLayerInfo(layerNumber, codeSectionStartSymbol, initialMethodIdToOffsetMap, initialMethodIdToMethodNameMap); } } @@ -231,7 +283,7 @@ public void afterCompilation(AfterCompilationAccess access) { assert HostedDynamicLayerInfo.singleton().verifyUniqueOffsets(config.getMethods()); for (var entry : config.getCodeCache().getOrderedCompilations()) { - HostedDynamicLayerInfo.singleton().registerOffset(entry.getLeft()); + HostedDynamicLayerInfo.singleton().registerCompilation(entry.getLeft()); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 1ece4bf75140..f94376e25086 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -32,16 +32,10 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; -import org.graalvm.collections.Pair; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider; import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; @@ -53,8 +47,6 @@ import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.code.ImageCodeInfo; import com.oracle.svm.core.deopt.Deoptimizer; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; -import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.code.CustomCallingConventionMethod; import com.oracle.svm.core.graal.code.ExplicitCallingConvention; import com.oracle.svm.core.graal.code.StubCallingConvention; @@ -155,7 +147,7 @@ static HostedMethod create(HostedUniverse universe, AnalysisMethod wrapped, Host private static HostedMethod create0(AnalysisMethod wrapped, HostedType holder, ResolvedSignature signature, ConstantPool constantPool, ExceptionHandler[] handlers, MultiMethodKey key, Map multiMethodMap, LocalVariableTable localVariableTable) { - Function> nameGenerator = (collisionCount) -> { + Function nameGenerator = (collisionCount) -> { String name = wrapped.wrapped.getName(); // want name w/o any multimethodkey suffix if (key != ORIGINAL_METHOD) { name += StableMethodNameFormatter.MULTI_METHOD_KEY_SEPARATOR + key; @@ -165,12 +157,12 @@ private static HostedMethod create0(AnalysisMethod wrapped, HostedType holder, R } String uniqueShortName = SubstrateUtil.uniqueShortName(holder.getJavaClass().getClassLoader(), holder, name, signature, wrapped.isConstructor()); - return Pair.create(name, uniqueShortName); + return new HostedMethodNameFactory.MethodNameInfo(name, uniqueShortName); }; - Pair names = ImageSingletons.lookup(HostedMethodNameFactory.class).createNames(nameGenerator); + HostedMethodNameFactory.MethodNameInfo names = HostedMethodNameFactory.singleton().createNames(nameGenerator, wrapped); - return new HostedMethod(wrapped, holder, signature, constantPool, handlers, names.getLeft(), names.getRight(), localVariableTable, key, multiMethodMap); + return new HostedMethod(wrapped, holder, signature, constantPool, handlers, names.name(), names.uniqueShortName(), localVariableTable, key, multiMethodMap); } private static LocalVariableTable createLocalVariableTable(HostedUniverse universe, AnalysisMethod wrapped) { @@ -627,31 +619,3 @@ public Collection getAllMultiMethods() { } } } - -@Platforms(Platform.HOSTED_ONLY.class) -@AutomaticallyRegisteredFeature -class HostedMethodNameFactory implements InternalFeature { - Map methodNameCount = new ConcurrentHashMap<>(); - Set uniqueShortNames = ConcurrentHashMap.newKeySet(); - - Pair createNames(Function> nameGenerator) { - Pair result = nameGenerator.apply(0); - - int collisionCount = methodNameCount.merge(result.getRight(), 0, (oldValue, value) -> oldValue + 1); - - if (collisionCount != 0) { - result = nameGenerator.apply(collisionCount); - } - - boolean added = uniqueShortNames.add(result.getRight()); - VMError.guarantee(added, "failed to generate uniqueShortName for HostedMethod: %s", result.getRight()); - - return result; - } - - @Override - public void afterCompilation(AfterCompilationAccess access) { - methodNameCount = null; - uniqueShortNames = null; - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethodNameFactory.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethodNameFactory.java new file mode 100644 index 000000000000..2468bc8d8f83 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethodNameFactory.java @@ -0,0 +1,86 @@ +/* + * 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.meta; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; + +@AutomaticallyRegisteredFeature +public class HostedMethodNameFactory implements InternalFeature { + private Map methodNameCount = new ConcurrentHashMap<>(); + private Set uniqueShortNames = ConcurrentHashMap.newKeySet(); + private final boolean buildingExtensionLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); + private Set reservedUniqueShortNames = buildingExtensionLayer ? HostedDynamicLayerInfo.singleton().getReservedNames() : null; + + public record MethodNameInfo(String name, String uniqueShortName) { + } + + public static HostedMethodNameFactory singleton() { + return ImageSingletons.lookup(HostedMethodNameFactory.class); + } + + MethodNameInfo createNames(Function nameGenerator, AnalysisMethod aMethod) { + MethodNameInfo result = buildingExtensionLayer ? HostedDynamicLayerInfo.singleton().loadMethodNameInfo(aMethod) : null; + if (result == null) { + MethodNameInfo initialName = nameGenerator.apply(0); + result = initialName; + + do { + int collisionCount = methodNameCount.merge(initialName.uniqueShortName(), 0, (oldValue, value) -> oldValue + 1); + if (collisionCount != 0) { + result = nameGenerator.apply(collisionCount); + } + /* + * Redo if the short name is reserved. + */ + } while (buildingExtensionLayer && reservedUniqueShortNames.contains(result.uniqueShortName())); + } else { + assert reservedUniqueShortNames.contains(result.uniqueShortName()) : result; + } + + boolean added = uniqueShortNames.add(result.uniqueShortName()); + VMError.guarantee(added, "failed to generate uniqueShortName for HostedMethod: %s", result.uniqueShortName()); + + return result; + } + + @Override + public void afterCompilation(AfterCompilationAccess access) { + methodNameCount = null; + uniqueShortNames = null; + reservedUniqueShortNames = null; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 09ca74350909..c4734139e892 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -89,6 +89,7 @@ import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.UserError; @@ -1125,6 +1126,18 @@ private static void registerVMConfigurationPlugins(InvocationPlugins plugins) { public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode) { Class key = constantObjectParameter(b, targetMethod, 0, Class.class, classNode); boolean result = ImageSingletons.contains(key); + if (!result && ImageLayerBuildingSupport.buildingImageLayer()) { + if (ApplicationLayerOnlyImageSingleton.class.isAssignableFrom(key) || MultiLayeredImageSingleton.class.isAssignableFrom(key)) { + /* + * ApplicationLayerOnlyImageSingletons and the array representation of a + * MultiLayeredImageSingleton will only be created in the final layer. + * However, we assume they exist in all layers. If lookup/getAllLayers is + * called on this key, then our infrastructure will ensure it is either + * created in the application layer or produce a buildtime error. + */ + result = true; + } + } b.addPush(JavaKind.Boolean, ConstantNode.forBoolean(result)); return true; }