diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 2e4a60a95a12..9a66ef69be47 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -28,6 +28,7 @@ import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; @@ -238,4 +239,6 @@ public void clearInThread() { public Object getConfiguration() { return null; } + + public abstract Comparator getTypeComparator(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java index 4c9bc51017b9..27c60b236312 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/AbstractAnalysisResultsBuilder.java @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.stream.Stream; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.PointstoOptions; @@ -38,6 +39,7 @@ import jdk.vm.ci.meta.JavaMethodProfile; import jdk.vm.ci.meta.JavaTypeProfile; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.TriState; public abstract class AbstractAnalysisResultsBuilder { @@ -126,9 +128,12 @@ private JavaTypeProfile cachedTypeProfile(JavaTypeProfile[] cache, int cacheIdx, private JavaTypeProfile createTypeProfile(TypeState typeState) { double probability = 1d / typeState.typesCount(); - JavaTypeProfile.ProfiledType[] pitems = typeState.typesStream(bb) - .map(analysisType -> converter == null ? analysisType : converter.lookup(analysisType)) - .sorted() + + Stream stream = typeState.typesStream(bb); + if (converter != null) { + stream = stream.map(converter::lookup).sorted(converter.hostVM().getTypeComparator()); + } + JavaTypeProfile.ProfiledType[] pitems = stream .map(type -> new JavaTypeProfile.ProfiledType(type, probability)) .toArray(JavaTypeProfile.ProfiledType[]::new); diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java index 734cef105cfe..6ca7fab14c81 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java @@ -44,7 +44,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiFunction; -import com.oracle.svm.shadowed.org.bytedeco.llvm.global.LLVM; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.core.common.LIRKind; @@ -83,7 +82,6 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.c.constant.CEnum; import org.graalvm.nativeimage.c.function.CEntryPoint; -import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.FrameAccess; import com.oracle.svm.core.ReservedRegisters; @@ -125,6 +123,8 @@ import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMBasicBlockRef; import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMTypeRef; import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMValueRef; +import com.oracle.svm.shadowed.org.bytedeco.llvm.global.LLVM; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.code.CallingConvention; @@ -186,7 +186,7 @@ public class LLVMGenerator implements LIRGeneratorTool, SubstrateLIRGenerator { this.lirKindTool = new LLVMUtils.LLVMKindTool(builder); this.debugInfoPrinter = new DebugInfoPrinter(this, debugLevel); - this.functionName = SubstrateUtil.uniqueShortName(method); + this.functionName = ((HostedMethod) method).getUniqueShortName(); this.isEntryPoint = isEntryPoint(method); this.modifiesSpecialRegisters = modifiesSpecialRegisters(graph); @@ -291,7 +291,7 @@ byte[] getBitcode() { } private static String getFunctionName(ResolvedJavaMethod method) { - return SubstrateUtil.uniqueShortName(method); + return ((HostedMethod) method).getUniqueShortName(); } private static boolean isEntryPoint(ResolvedJavaMethod method) { 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 cd57cdf50926..a61617d50c52 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 @@ -63,7 +63,6 @@ import com.oracle.objectfile.ObjectFile.Element; import com.oracle.objectfile.SectionName; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.c.CGlobalDataImpl; import com.oracle.svm.core.graal.code.CGlobalDataInfo; import com.oracle.svm.core.graal.code.CGlobalDataReference; @@ -224,7 +223,7 @@ private void linkCompiledBatches(BatchExecutor executor, DebugContext debug, int executor.forEach(getOrderedCompilations(), pair -> (debugContext) -> { HostedMethod method = pair.getLeft(); - int offset = textSectionInfo.getOffset(SubstrateUtil.uniqueShortName(method)); + int offset = textSectionInfo.getOffset(method.getUniqueShortName()); int nextFunctionStartOffset = textSectionInfo.getNextOffset(offset); int functionSize = nextFunctionStartOffset - offset; diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/util/LLVMObjectFileReader.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/util/LLVMObjectFileReader.java index f9ac10d52f8d..0ea11174e79b 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/util/LLVMObjectFileReader.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/util/LLVMObjectFileReader.java @@ -43,10 +43,10 @@ import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.SectionName; -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.graal.llvm.LLVMGenerator; import com.oracle.svm.core.graal.llvm.LLVMNativeImageCodeCache.StackMapDumper; import com.oracle.svm.core.heap.SubstrateReferenceMap; +import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.shadowed.org.bytedeco.javacpp.BytePointer; import com.oracle.svm.shadowed.org.bytedeco.javacpp.Pointer; import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMMemoryBufferRef; @@ -161,7 +161,7 @@ private LLVMStackMapInfo readStackMapSection(LLVMSectionIteratorRef sectionItera } public void readStackMap(LLVMStackMapInfo info, CompilationResult compilation, ResolvedJavaMethod method, int id) { - String methodSymbolName = SYMBOL_PREFIX + SubstrateUtil.uniqueShortName(method); + String methodSymbolName = SYMBOL_PREFIX + ((HostedMethod) method).getUniqueShortName(); long startPatchpointID = compilation.getInfopoints().stream().filter(ip -> ip.reason == InfopointReason.METHOD_START).findFirst() .orElseThrow(() -> new GraalError("no method start infopoint: " + methodSymbolName)).pcOffset; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java index 755d5edea3d9..d81b9092c0df 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateUtil.java @@ -60,6 +60,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Signature; import jdk.vm.ci.services.Services; public class SubstrateUtil { @@ -278,21 +279,38 @@ public static String digest(String value) { * name includes a digest of the fully qualified method name, which ensures uniqueness. */ public static String uniqueShortName(ResolvedJavaMethod m) { - StringBuilder fullName = new StringBuilder(); - fullName.append(m.getDeclaringClass().toClassName()).append(".").append(m.getName()).append("("); - for (int i = 0; i < m.getSignature().getParameterCount(false); i++) { - fullName.append(m.getSignature().getParameterType(i, null).toClassName()).append(","); + return uniqueShortName("", m.getDeclaringClass(), m.getName(), m.getSignature(), m.isConstructor()); + } + + public static String uniqueShortName(String loaderNameAndId, ResolvedJavaType declaringClass, String methodName, Signature methodSignature, boolean isConstructor) { + StringBuilder sb = new StringBuilder(loaderNameAndId); + sb.append(declaringClass.toClassName()).append(".").append(methodName).append("("); + for (int i = 0; i < methodSignature.getParameterCount(false); i++) { + sb.append(methodSignature.getParameterType(i, null).toClassName()).append(","); } - fullName.append(')'); - if (!m.isConstructor()) { - fullName.append(m.getSignature().getReturnType(null).toClassName()); + sb.append(')'); + if (!isConstructor) { + sb.append(methodSignature.getReturnType(null).toClassName()); } - return stripPackage(m.getDeclaringClass().toJavaName()) + "_" + - (m.isConstructor() ? "constructor" : m.getName()) + "_" + - SubstrateUtil.digest(fullName.toString()); + return stripPackage(declaringClass.toJavaName()) + "_" + + (isConstructor ? "constructor" : methodName) + "_" + + SubstrateUtil.digest(sb.toString()); } + public static String classLoaderNameAndId(ClassLoader loader) { + if (loader == null) { + return ""; + } + try { + return (String) classLoaderNameAndId.get(loader); + } catch (IllegalAccessException e) { + throw VMError.shouldNotReachHere("Cannot reflectively access ClassLoader.nameAndId"); + } + } + + private static final Field classLoaderNameAndId = ReflectionUtil.lookupField(ClassLoader.class, "nameAndId"); + /** * Returns a short, reasonably descriptive, but still unique name for the provided * {@link Method}, {@link Constructor}, or {@link Field}. The name includes a digest of the diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 7f875b7df427..610d6bb7b5d2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -31,6 +31,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Modifier; import java.util.Collections; +import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -65,7 +66,6 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.RelocatedPointer; -import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.HostVM; @@ -109,10 +109,12 @@ import com.oracle.svm.hosted.code.UninterruptibleAnnotationChecker; import com.oracle.svm.hosted.heap.PodSupport; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.phases.AnalysisGraphBuilderPhase; import com.oracle.svm.hosted.phases.ImplicitAssertionsPhase; import com.oracle.svm.hosted.phases.InlineBeforeAnalysisPolicyImpl; import com.oracle.svm.hosted.substitute.UnsafeAutomaticSubstitutionProcessor; +import com.oracle.svm.util.GuardedAnnotationAccess; import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.ResolvedJavaField; @@ -743,4 +745,11 @@ public boolean neverInlineTrivial(AnalysisMethod caller, AnalysisMethod callee) } return false; } + + @Override + public Comparator getTypeComparator() { + return (Comparator) (o1, o2) -> { + return HostedUniverse.TYPE_COMPARATOR.compare((HostedType) o1, (HostedType) o2); + }; + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index cc02f0a33ef7..f308c884ed13 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -646,7 +646,6 @@ private void parseDeoptimizationTargetMethods() { universe.getMethods().stream() .filter(method -> method.getWrapped().isImplementationInvoked() && canDeoptForTesting(method)) .forEach(this::ensureParsedForDeoptTesting); - } private void ensureParsedForDeoptTesting(HostedMethod method) { @@ -1675,7 +1674,7 @@ private static void insertDeoptTests(HostedMethod method, StructuredGraph graph) } public Map getCompilationResults() { - Map result = new TreeMap<>(); + Map result = new TreeMap<>(HostedUniverse.METHOD_COMPARATOR); for (Entry entry : compilations.entrySet()) { result.put(entry.getKey(), entry.getValue().result); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java index 30a96a7a0609..1273d2eef846 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedArrayClass.java @@ -92,10 +92,4 @@ public boolean isLocal() { public boolean isMember() { return false; } - - @Override - int compareToEqualClass(HostedType other) { - assert getClass().equals(other.getClass()); - return getComponentType().compareTo(other.getComponentType()); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java index 702dc883a152..0fdfd06d7f30 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java @@ -41,7 +41,7 @@ /** * Store the compile-time information for a field in the Substrate VM, such as the field offset. */ -public class HostedField implements OriginalFieldProvider, SharedField, Comparable, WrappedJavaField, AnnotationWrapper { +public class HostedField implements OriginalFieldProvider, SharedField, WrappedJavaField, AnnotationWrapper { private final HostedUniverse universe; private final HostedMetaAccess metaAccess; @@ -198,20 +198,6 @@ public JavaKind getStorageKind() { return getType().getStorageKind(); } - @Override - public int compareTo(HostedField other) { - /* - * Order by JavaKind. This is required, since we want instance fields of the same size and - * kind consecutive. - */ - int result = other.getJavaKind().ordinal() - this.getJavaKind().ordinal(); - /* - * If the kind is the same, i.e., result == 0, we return 0 so that the sorting keeps the - * order unchanged and therefore keeps the field order we get from the hosting VM. - */ - return result; - } - @Override public Field getJavaField() { return OriginalFieldProvider.getJavaField(getDeclaringClass().universe.getSnippetReflection(), wrapped); 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 18eef09a9d6f..161828267ed2 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 @@ -66,8 +66,9 @@ import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.SpeculationLog; -public class HostedMethod implements SharedMethod, WrappedJavaMethod, GraphProvider, JavaMethodContext, Comparable, OriginalMethodProvider { +public final class HostedMethod implements SharedMethod, WrappedJavaMethod, GraphProvider, JavaMethodContext, OriginalMethodProvider { + public static final String METHOD_NAME_COLLISION_SUFFIX = "*"; public static final String METHOD_NAME_DEOPT_SUFFIX = "**"; public final AnalysisMethod wrapped; @@ -76,8 +77,8 @@ public class HostedMethod implements SharedMethod, WrappedJavaMethod, GraphProvi private final Signature signature; private final ConstantPool constantPool; private final ExceptionHandler[] handlers; - protected StaticAnalysisResults staticAnalysisResults; - protected int vtableIndex = -1; + StaticAnalysisResults staticAnalysisResults; + int vtableIndex = -1; /** * The address offset of the compiled code relative to the code of the first method in the @@ -91,42 +92,61 @@ public class HostedMethod implements SharedMethod, WrappedJavaMethod, GraphProvi * All concrete methods that can actually be called when calling this method. This includes all * overridden methods in subclasses, as well as this method if it is non-abstract. */ - protected HostedMethod[] implementations; + HostedMethod[] implementations; public final CompilationInfo compilationInfo; private final LocalVariableTable localVariableTable; + private final String name; private final String uniqueShortName; - public HostedMethod(HostedUniverse universe, AnalysisMethod wrapped, HostedType holder, Signature signature, ConstantPool constantPool, ExceptionHandler[] handlers, HostedMethod deoptOrigin) { + public static HostedMethod create(HostedUniverse universe, AnalysisMethod wrapped, HostedType holder, Signature signature, + ConstantPool constantPool, ExceptionHandler[] handlers, HostedMethod deoptOrigin) { + LocalVariableTable localVariableTable = createLocalVariableTable(universe, wrapped); + String name = deoptOrigin != null ? wrapped.getName() + METHOD_NAME_DEOPT_SUFFIX : wrapped.getName(); + String uniqueShortName = SubstrateUtil.uniqueShortName(SubstrateUtil.classLoaderNameAndId(holder.getJavaClass().getClassLoader()), holder, name, signature, wrapped.isConstructor()); + int collisionCount = universe.uniqueHostedMethodNames.merge(uniqueShortName, 0, (oldValue, value) -> oldValue + 1); + if (collisionCount > 0) { + name = name + METHOD_NAME_COLLISION_SUFFIX + collisionCount; + uniqueShortName = SubstrateUtil.uniqueShortName(SubstrateUtil.classLoaderNameAndId(holder.getJavaClass().getClassLoader()), holder, name, signature, wrapped.isConstructor()); + } + return new HostedMethod(wrapped, holder, signature, constantPool, handlers, deoptOrigin, name, uniqueShortName, localVariableTable); + } + + private static LocalVariableTable createLocalVariableTable(HostedUniverse universe, AnalysisMethod wrapped) { + LocalVariableTable lvt = wrapped.getLocalVariableTable(); + if (lvt == null) { + return null; + } + try { + Local[] origLocals = lvt.getLocals(); + Local[] newLocals = new Local[origLocals.length]; + for (int i = 0; i < newLocals.length; ++i) { + Local origLocal = origLocals[i]; + JavaType origType = origLocal.getType(); + if (!universe.contains(origType)) { + throw new UnsupportedFeatureException("No HostedType for given AnalysisType"); + } + HostedType newType = universe.lookup(origType); + newLocals[i] = new Local(origLocal.getName(), newType, origLocal.getStartBCI(), origLocal.getEndBCI(), origLocal.getSlot()); + } + return new LocalVariableTable(newLocals); + } catch (UnsupportedFeatureException e) { + return null; + } + } + + private HostedMethod(AnalysisMethod wrapped, HostedType holder, Signature signature, ConstantPool constantPool, + ExceptionHandler[] handlers, HostedMethod deoptOrigin, String name, String uniqueShortName, LocalVariableTable localVariableTable) { this.wrapped = wrapped; this.holder = holder; this.signature = signature; this.constantPool = constantPool; this.handlers = handlers; this.compilationInfo = new CompilationInfo(this, deoptOrigin); - this.uniqueShortName = SubstrateUtil.uniqueShortName(this); - - LocalVariableTable newLocalVariableTable = null; - if (wrapped.getLocalVariableTable() != null) { - try { - Local[] origLocals = wrapped.getLocalVariableTable().getLocals(); - Local[] newLocals = new Local[origLocals.length]; - for (int i = 0; i < newLocals.length; ++i) { - Local origLocal = origLocals[i]; - JavaType origType = origLocal.getType(); - if (!universe.contains(origType)) { - throw new UnsupportedFeatureException("No HostedType for given AnalysisType"); - } - HostedType newType = universe.lookup(origType); - newLocals[i] = new Local(origLocal.getName(), newType, origLocal.getStartBCI(), origLocal.getEndBCI(), origLocal.getSlot()); - } - newLocalVariableTable = new LocalVariableTable(newLocals); - } catch (UnsupportedFeatureException e) { - newLocalVariableTable = null; - } - } - localVariableTable = newLocalVariableTable; + this.localVariableTable = localVariableTable; + this.name = name; + this.uniqueShortName = uniqueShortName; } @Override @@ -260,10 +280,7 @@ public boolean hasCalleeSavedRegisters() { @Override public String getName() { - if (compilationInfo.isDeoptTarget()) { - return wrapped.getName() + METHOD_NAME_DEOPT_SUFFIX; - } - return wrapped.getName(); + return name; } @Override @@ -441,47 +458,6 @@ public int hashCode() { return wrapped.hashCode(); } - @Override - public int compareTo(HostedMethod other) { - if (this.equals(other)) { - return 0; - } - - /* - * Sort deoptimization targets towards the end of the code cache. They are rarely executed, - * and we do not want a deoptimization target as the first method (because offset 0 means no - * deoptimization target available). - */ - int result = Boolean.compare(this.compilationInfo.isDeoptTarget(), other.compilationInfo.isDeoptTarget()); - - if (result == 0) { - result = this.getDeclaringClass().compareTo(other.getDeclaringClass()); - } - if (result == 0) { - result = this.getName().compareTo(other.getName()); - } - if (result == 0) { - result = this.getSignature().getParameterCount(false) - other.getSignature().getParameterCount(false); - } - if (result == 0) { - for (int i = 0; i < this.getSignature().getParameterCount(false); i++) { - result = ((HostedType) this.getSignature().getParameterType(i, null)).compareTo((HostedType) other.getSignature().getParameterType(i, null)); - if (result != 0) { - break; - } - } - } - if (result == 0) { - result = ((HostedType) this.getSignature().getReturnType(null)).compareTo((HostedType) other.getSignature().getReturnType(null)); - } - /* - * Note that the result can still be 0 at this point: with class substitutions or incomplete - * classpath, two separate methods can have the same signature. Not ordering such methods is - * fine. GR-32976 should remove the sorting altogether. - */ - return result; - } - @Override public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(getDeclaringClass().universe.getSnippetReflection(), wrapped); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java index 256628ae8952..096df6b96ff9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedPrimitiveType.java @@ -93,10 +93,4 @@ public boolean isLocal() { public boolean isMember() { return false; } - - @Override - int compareToEqualClass(HostedType other) { - assert getClass().equals(other.getClass()); - return getJavaKind().ordinal() - other.getJavaKind().ordinal(); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index 77cc90f9bcb9..498ca532d4f4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.hosted.meta; -import static com.oracle.svm.core.util.VMError.shouldNotReachHere; - import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; @@ -42,7 +40,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -public abstract class HostedType implements SharedType, WrappedJavaType, Comparable, OriginalClassProvider { +public abstract class HostedType implements SharedType, WrappedJavaType, OriginalClassProvider { protected final HostedUniverse universe; protected final AnalysisType wrapped; @@ -430,36 +428,4 @@ public void setEnclosingType(HostedType enclosingType) { public Class getJavaClass() { return OriginalClassProvider.getJavaClass(universe.getSnippetReflection(), wrapped); } - - @Override - public int compareTo(HostedType other) { - if (this.equals(other)) { - return 0; - } - if (this.getClass().equals(other.getClass())) { - return compareToEqualClass(other); - } - int result = this.ordinal() - other.ordinal(); - assert result != 0 : "Types not distinguishable: " + this + ", " + other; - return result; - } - - int compareToEqualClass(HostedType other) { - assert getClass().equals(other.getClass()); - return getName().compareTo(other.getName()); - } - - private int ordinal() { - if (isInterface()) { - return 4; - } else if (isArray()) { - return 3; - } else if (isInstanceClass()) { - return 2; - } else if (getJavaKind() != JavaKind.Object) { - return 1; - } else { - throw shouldNotReachHere(); - } - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java index d765af45177d..43fa6ff94b29 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java @@ -27,10 +27,13 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Comparator; import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.nodes.StructuredGraph; @@ -52,10 +55,12 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; @@ -286,8 +291,10 @@ public class HostedUniverse implements Universe { protected EnumMap kindToType = new EnumMap<>(JavaKind.class); protected List orderedTypes; - protected List orderedMethods; protected List orderedFields; + protected List orderedMethods; + + Map uniqueHostedMethodNames = new ConcurrentHashMap<>(); public HostedUniverse(Inflation bb) { this.bb = bb; @@ -304,13 +311,15 @@ public HostedInstanceClass getObjectClass() { return result; } - public synchronized HostedMethod createDeoptTarget(HostedMethod method) { - if (method.compilationInfo.getDeoptTargetMethod() == null) { - HostedMethod deoptTarget = new HostedMethod(this, method.getWrapped(), method.getDeclaringClass(), method.getSignature(), method.getConstantPool(), method.getExceptionHandlers(), method); - assert method.staticAnalysisResults != null; - deoptTarget.staticAnalysisResults = method.staticAnalysisResults; + public synchronized HostedMethod createDeoptTarget(HostedMethod deoptOrigin) { + assert !deoptOrigin.isDeoptTarget(); + if (deoptOrigin.compilationInfo.getDeoptTargetMethod() == null) { + HostedMethod deoptTarget = HostedMethod.create(this, deoptOrigin.getWrapped(), deoptOrigin.getDeclaringClass(), + deoptOrigin.getSignature(), deoptOrigin.getConstantPool(), deoptOrigin.getExceptionHandlers(), deoptOrigin); + assert deoptOrigin.staticAnalysisResults != null; + deoptTarget.staticAnalysisResults = deoptOrigin.staticAnalysisResults; } - return method.compilationInfo.getDeoptTargetMethod(); + return deoptOrigin.compilationInfo.getDeoptTargetMethod(); } public boolean contains(JavaType type) { @@ -449,4 +458,129 @@ public ResolvedJavaMethod resolveSubstitution(ResolvedJavaMethod method) { public HostedType objectType() { return types.get(bb.getUniverse().objectType()); } + + public static final Comparator TYPE_COMPARATOR = new TypeComparator(); + + private static final class TypeComparator implements Comparator { + + @Override + public int compare(HostedType o1, HostedType o2) { + if (o1.equals(o2)) { + return 0; + } + + if (!o1.getClass().equals(o2.getClass())) { + int result = Integer.compare(ordinal(o1), ordinal(o2)); + VMError.guarantee(result != 0, "HostedType objects not distinguishable by ordinal number: " + o1 + ", " + o2); + return result; + } + + if (o1.isPrimitive() && o2.isPrimitive()) { + assert o1 instanceof HostedPrimitiveType && o2 instanceof HostedPrimitiveType; + int result = o1.getJavaKind().compareTo(o2.getJavaKind()); + VMError.guarantee(result != 0, "HostedPrimitiveType objects not distinguishable by javaKind: " + o1 + ", " + o2); + return result; + } + + if (o1.isArray() && o2.isArray()) { + assert o1 instanceof HostedArrayClass && o2 instanceof HostedArrayClass; + int result = compare(o1.getComponentType(), o2.getComponentType()); + VMError.guarantee(result != 0, "HostedArrayClass objects not distinguishable by componentType: " + o1 + ", " + o2); + return result; + } + + int result = o1.getName().compareTo(o2.getName()); + if (result != 0) { + return result; + } + + ClassLoader l1 = Optional.ofNullable(o1.getJavaClass()).map(Class::getClassLoader).orElse(null); + ClassLoader l2 = Optional.ofNullable(o2.getJavaClass()).map(Class::getClassLoader).orElse(null); + result = SubstrateUtil.classLoaderNameAndId(l1).compareTo(SubstrateUtil.classLoaderNameAndId(l2)); + VMError.guarantee(result != 0, "HostedType objects not distinguishable by name and classloader: " + o1 + ", " + o2); + return result; + } + + private static int ordinal(HostedType type) { + if (type.isInterface()) { + return 4; + } else if (type.isArray()) { + return 3; + } else if (type.isInstanceClass()) { + return 2; + } else if (type.getJavaKind() != JavaKind.Object) { + return 1; + } else { + throw VMError.shouldNotReachHere(); + } + } + } + + public static final Comparator METHOD_COMPARATOR = new MethodComparator(TYPE_COMPARATOR); + + private static final class MethodComparator implements Comparator { + + private final Comparator typeComparator; + + private MethodComparator(Comparator typeComparator) { + this.typeComparator = typeComparator; + } + + @Override + public int compare(HostedMethod o1, HostedMethod o2) { + if (o1.equals(o2)) { + return 0; + } + + /* + * Sort deoptimization targets towards the end of the code cache. They are rarely + * executed, and we do not want a deoptimization target as the first method (because + * offset 0 means no deoptimization target available). + */ + int result = Boolean.compare(o1.compilationInfo.isDeoptTarget(), o2.compilationInfo.isDeoptTarget()); + if (result != 0) { + return result; + } + + result = typeComparator.compare(o1.getDeclaringClass(), o2.getDeclaringClass()); + if (result != 0) { + return result; + } + + result = o1.getName().compareTo(o2.getName()); + if (result != 0) { + return result; + } + + Signature signature1 = o1.getSignature(); + Signature signature2 = o2.getSignature(); + int parameterCount1 = signature1.getParameterCount(false); + result = Integer.compare(parameterCount1, signature2.getParameterCount(false)); + if (result != 0) { + return result; + } + + for (int i = 0; i < parameterCount1; i++) { + result = typeComparator.compare((HostedType) signature1.getParameterType(i, null), (HostedType) signature2.getParameterType(i, null)); + if (result != 0) { + return result; + } + } + + result = typeComparator.compare((HostedType) signature1.getReturnType(null), (HostedType) signature2.getReturnType(null)); + if (result != 0) { + return result; + } + + throw VMError.shouldNotReachHere("HostedMethod objects not distinguishable: " + o1 + ", " + o2); + } + } + + /* + * Order by JavaKind. This is required, since we want instance fields of the same size and kind + * consecutive. If the kind is the same, i.e., result == 0, we return 0 so that the sorting + * keeps the order unchanged and therefore keeps the field order we get from the hosting VM. + */ + static final Comparator FIELD_COMPARATOR_RELAXED = Comparator.comparing(HostedField::getJavaKind).reversed(); + } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java index bbc213d7e1af..d80de19f4573 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java @@ -242,7 +242,7 @@ private Map> computeSubtypeInformation() { /* Convert values into a sorted list. */ Map> result = new HashMap<>(); - subtypes.forEach((k, v) -> result.put(k, v.stream().sorted().collect(Collectors.toList()))); + subtypes.forEach((k, v) -> result.put(k, v.stream().sorted(HostedUniverse.TYPE_COMPARATOR).collect(Collectors.toList()))); return result; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 70398bc3ff5b..57ade8549793 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -180,10 +180,11 @@ public void build(DebugContext debug) { processFieldLocations(); + hUniverse.uniqueHostedMethodNames.clear(); hUniverse.orderedMethods = new ArrayList<>(hUniverse.methods.values()); - Collections.sort(hUniverse.orderedMethods); + Collections.sort(hUniverse.orderedMethods, HostedUniverse.METHOD_COMPARATOR); hUniverse.orderedFields = new ArrayList<>(hUniverse.fields.values()); - Collections.sort(hUniverse.orderedFields); + Collections.sort(hUniverse.orderedFields, HostedUniverse.FIELD_COMPARATOR_RELAXED); profilingInformationBuildTask.join(); } } @@ -294,7 +295,7 @@ private void makeMethod(AnalysisMethod aMethod) { sHandlers[i] = new ExceptionHandler(h.getStartBCI(), h.getEndBCI(), h.getHandlerBCI(), h.catchTypeCPI(), catchType); } - HostedMethod sMethod = new HostedMethod(hUniverse, aMethod, holder, signature, constantPool, sHandlers, null); + HostedMethod sMethod = HostedMethod.create(hUniverse, aMethod, holder, signature, constantPool, sHandlers, null); assert !hUniverse.methods.containsKey(aMethod); hUniverse.methods.put(aMethod, sMethod); @@ -442,7 +443,7 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, Host } // Sort so that a) all Object fields are consecutive, and b) bigger types come first. - Collections.sort(rawFields); + Collections.sort(rawFields, HostedUniverse.FIELD_COMPARATOR_RELAXED); int nextOffset = startSize; while (rawFields.size() > 0) { @@ -521,7 +522,7 @@ private void layoutStaticFields() { } // Sort so that a) all Object fields are consecutive, and b) bigger types come first. - Collections.sort(fields); + Collections.sort(fields, HostedUniverse.FIELD_COMPARATOR_RELAXED); ObjectLayout layout = ConfigurationValues.getObjectLayout(); @@ -586,7 +587,7 @@ private void collectDeclaredMethods() { for (HostedType type : hUniverse.getTypes()) { List list = methodsOfType[type.getTypeID()]; if (list != null) { - Collections.sort(list); + Collections.sort(list, HostedUniverse.METHOD_COMPARATOR); type.allDeclaredMethods = list.toArray(new HostedMethod[list.size()]); } else { type.allDeclaredMethods = noMethods; @@ -599,7 +600,7 @@ private void collectMethodImplementations() { // Reuse the implementations from the analysis method. method.implementations = hUniverse.lookup(method.wrapped.getImplementations()); - Arrays.sort(method.implementations); + Arrays.sort(method.implementations, HostedUniverse.METHOD_COMPARATOR); } }